Tôi có máy chủ đa luồng (nhóm luồng) đang xử lý một số lượng lớn yêu cầu (tối đa 500/giây cho một nút), sử dụng 20 chuỗi. Có một chuỗi trình nghe chấp nhận các kết nối đến và xếp hàng chúng để xử lý các luồng xử lý. Khi đáp ứng đã sẵn sàng, các luồng sau đó ghi ra máy khách và đóng socket. Tất cả dường như tốt cho đến gần đây, một chương trình thử nghiệm của khách hàng bắt đầu treo ngẫu nhiên sau khi đọc câu trả lời. Sau nhiều lần đào, có vẻ như phần đóng() từ máy chủ không thực sự ngắt kết nối ổ cắm. Tôi đã thêm một số bản in gỡ lỗi vào mã với số mô tả tập tin và tôi nhận được kiểu đầu ra này.close() không đóng đúng ổ cắm
Processing request for 21
Writing to 21
Closing 21
Giá trị trả về của gần() bằng 0, hoặc sẽ có một câu lệnh gỡ lỗi khác được in. Sau khi đầu ra này với một khách hàng bị treo, lsof đang hiển thị một kết nối đã được thiết lập.
MÁY CHỦ 8160 gốc 21U IPv4 32.754.237 TCP localhost: 9980-> localhost: 47.530 (ESTABLISHED)
KHÁCH HÀNG 17.747 gốc 12u IPv4 32.754.228 TCP localhost: 47530-> localhost: 9980 (ESTABLISHED)
Cứ như nếu máy chủ không bao giờ gửi trình tự tắt cho máy khách và trạng thái này bị treo cho đến khi máy khách bị giết, hãy để máy chủ ở trạng thái chờ gần như
MÁY CHỦ 8160 root 21u IPv4 32754237 TCP localhost: 9980-> localhost: 47530 (CLOSE_WAIT)
Ngoài ra nếu khách hàng có thời gian chờ được chỉ định, nó sẽ hết thời gian chờ thay vì treo. Tôi cũng có thể chạy theo cách thủ công
call close(21)
trong máy chủ từ gdb, sau đó khách hàng sẽ ngắt kết nối. Điều này xảy ra có thể một lần trong 50.000 yêu cầu, nhưng có thể không xảy ra trong thời gian dài.
Linux phiên bản: 2.6.21.7-2.fc8xen Centos phiên bản: 5.4 (Final)
hành động ổ cắm như sau
SERVER:
int client_socket; struct sockaddr_in client_addr; socklen_t client_len = sizeof (client_addr);
while(true) {
client_socket = accept(incoming_socket, (struct sockaddr *)&client_addr, &client_len);
if (client_socket == -1)
continue;
/* insert into queue here for threads to process */
}
Sau đó, chủ đề chọn ổ cắm và tạo phản hồi.
/* get client_socket from queue */
/* processing request here */
/* now set to blocking for write; was previously set to non-blocking for reading */
int flags = fcntl(client_socket, F_GETFL);
if (flags < 0)
abort();
if (fcntl(client_socket, F_SETFL, flags|O_NONBLOCK) < 0)
abort();
server_write(client_socket, response_buf, response_length);
server_close(client_socket);
server_write và server_close.
void server_write(int fd, char const *buf, ssize_t len) {
printf("Writing to %d\n", fd);
while(len > 0) {
ssize_t n = write(fd, buf, len);
if(n <= 0)
return;// I don't really care what error happened, we'll just drop the connection
len -= n;
buf += n;
}
}
void server_close(int fd) {
for(uint32_t i=0; i<10; i++) {
int n = close(fd);
if(!n) {//closed successfully
return;
}
usleep(100);
}
printf("Close failed for %d\n", fd);
}
KHÁCH HÀNG:
phía khách hàng đang sử dụng libcurl v 7.27.0
CURL *curl = curl_easy_init();
CURLcode res;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, write_tag);
res = curl_easy_perform(curl);
Không có gì lạ mắt, chỉ cần một kết nối curl cơ bản. Khách hàng bị treo trong tranfer.c (trong libcurl) vì ổ cắm không được coi là bị đóng. Nó đang chờ đợi thêm dữ liệu từ máy chủ.
Những điều tôi đã cố gắng cho đến nay:
Shutdown trước khi đóng cửa
shutdown(fd, SHUT_WR);
char buf[64];
while(read(fd, buf, 64) > 0);
/* then close */
Thiết SO_LINGER để đóng buộc trong 1 giây
struct linger l;
l.l_onoff = 1;
l.l_linger = 1;
if (setsockopt(client_socket, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1)
abort();
Những đã thực hiện có sự khác biệt. bất kì ý kiến nào đều được đánh giá cao.
EDIT - Điều này đã kết thúc là một vấn đề an toàn luồng bên trong thư viện hàng đợi khiến cho nhiều socket bị xử lý không thích hợp.
Bạn có 100% tích cực không có chủ đề nào khác có thể sử dụng ổ cắm khi bạn gọi 'đóng' trên nó? Làm thế nào để bạn đọc không bị chặn? –
Tôi e rằng tôi vừa đăng nhập ở đây và nhớ vấn đề này. Tôi phát hiện ra sau đó có một vấn đề an toàn thread trong một hàng đợi được sử dụng để vượt qua các kết nối xung quanh. Không có lỗi ở đây. Xin lỗi vì thông tin sai lạc. – DavidMFrey