2013-08-16 33 views
5

Có một chủ đề thích này:Làm thế nào để cho một chuỗi chặn trên recv() thoát một cách duyên dáng?

{ 
    ......  
    while (1) 
    { 
     recv(socket, buffer, sizeof(buffer), 0); 
     ...... 
    } 
    close(socket);   
} 

Bởi vì các chủ đề được chặn trên recv() gọi, làm sao tôi có thể để cho thread thoát duyên dáng?

+1

Kiểm tra giá trị trả về của 'recv' và' break' khi 0 hoặc âm? – cnicutar

+0

Bạn có hỏi liệu có cách nào để thực hiện thời gian chờ của cuộc gọi recv() sau một khoảng thời gian được đặt nếu nó không nhận được phản hồi không? – gwilkins

Trả lời

5

Bạn có thể gọi: -

shutdown(sock, SHUT_RDWR) //on the remote end 

Kiểm tra this ra.

+0

Kết thúc từ xa có thể đang trong quá trình khác trên máy chủ khác. –

2

Khai báo một lá cờ cảnh toàn cầu:

int bExit = 0; 

Hãy để phần cirtical kiểm tra nó:

while(1) 
{ 
    ssize_t result = recv(socket, buffer, sizeof(buffer), 0); 
    if ((-1 == result) && (EINTR == error) && bExit) 
    { 
    break; 
    } 

    ... 
} 

Để phá vỡ độc giả của bạn, đầu tiên đặt cờ thoát

bExit = 1; 

sau đó gửi tín hiệu cho chủ đề đọc

pthread_kill(pthreadReader, SIGUSR1); 

Lưu ý: gì tôi rời ra trong ví dụ này việc bảo vệ chống lại bExit truy cập đồng thời là.

Điều này có thể đạt được bằng cách sử dụng mutex hoặc khai báo thích hợp.

+2

Lưu ý rằng: _Nếu một cuộc gọi bị chặn đến một trong các giao diện sau (recv included), bị ngắt bởi bộ xử lý tín hiệu, sau đó cuộc gọi sẽ được tự động khởi động lại sau khi bộ xử lý tín hiệu trả về nếu cờ SA_RESTART được sử dụng; nếu không cuộc gọi sẽ thất bại với lỗi EINTR._ Ngoài ra, bạn đang bối rối 'errno' với giá trị trả về' recv() '. –

+0

@MaximYegorushkin: Cảm ơn đã chỉ cho tôi sự nhầm lẫn 'result' /' errno' .. Tôi bằng cách nào đó vẫn còn với 'pthread_ *' -API trả về 'errno'-values ​​nhưng thiết lập' errno'. Ngoài ra gợi ý về 'SA_RESTART' là quan trọng, tôi đã quên đề cập đến nó, cảm ơn lần nữa vì điều này. – alk

5

Không chặn trên recv. Thay vì thực hiện một khối trên 'chọn' và kiểm tra fdset đầu vào để xác định xem có bất cứ điều gì để đọc ở nơi đầu tiên.

Nếu có dữ liệu cần đọc, hãy gọi recv. Nếu không, lặp lại, kiểm tra biến Boolean volitile 'isrunning' được thiết lập bởi thread khác khi tắt máy đã được khởi tạo.

+0

Tôi nghĩ rằng hiệu quả của phương pháp này là thấp, phải không? –

+2

Thực ra nó rất cao. Nó cho phép bạn dịch vụ nhiều ổ cắm cùng một lúc cho mỗi luồng. Tôi đã sử dụng phương pháp này trên các máy chủ lưu trữ hàng nghìn kết nối. Khối 64 ổ cắm được quản lý thông qua lựa chọn trên mỗi luồng. Ngay cả ở mức đầy đủ, tải CPU vẫn ở mức ít hơn mười phần trăm. –

+0

: cảm ơn! Theo tôi, mô hình chương trình của bạn không phải là tất cả các luồng phục vụ mọi ổ cắm, mà là một luồng phục vụ nhiều ổ cắm. Bạn có nghĩ rằng mỗi thread phục vụ tất cả các ổ cắm hiệu quả hơn? –

0

Ngoài ra, thiết lập ổ cắm của bạn được nonblocking:

int listen_sd = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); 
if (listen_sd < 0) { 
    perror("socket() failed"); 
} 

int on = 1; 

//set socket to be non-blocking 
int rc = ioctl(listen_sd, FIONBIO,(char *)&on); 
if (rc < 0) { 
    perror("ioctl() failed"); 
    close(listen_sd); 
} 

Sau đó, bạn có thể gọi recv() trên socket, và nó sẽ không chặn. Nếu không có gì để đọc sau đó là biến toàn cục ERRNO được thiết lập để hằng EWOULDBLOCK

int rc = recv(listen_sd, buffer, sizeof(buffer), 0); 
if (rc < 0) { 
    if (errno != EWOULDBLOCK) { 
     perror("recv() failed"); 
    } 
} 
+0

'if (errno! = EWOULDBLOCK) {' nên tốt hơn là 'if ((errno! = EWOULDBLOCK) && (errno! = EAGAIN)) {'. – alk

+0

@alk Họ có cùng giá trị khi cả hai được xác định. – EJP

2

Khai báo một số 'stop' boolean, kiểm tra xem nó sau mỗi recv() trở lại và chấm dứt nếu nó được đặt. Để tắt, thiết lập bool và đóng socket từ một thread khác. Chặn recv() sẽ trả về 'ngay lập tức' với lỗi, nhưng không quan trọng 'cos bạn sắp chấm dứt anyway :)

+4

Lưu ý điều kiện chủng tộc tiềm năng ở đây nếu luồng bao giờ tự đóng chính socket: sau đó lệnh gọi của thread khác để đóng() có thể vô tình đóng một số ổ cắm khác đã được tạo sau đó bằng cùng một số socket. –

+0

Có thể tốt hơn để gửi tín hiệu đến chủ đề khác, nếu hệ điều hành của bạn không khởi động lại syscalls sau khi một tín hiệu hoặc một cái gì đó wacky như thế. – cHao

1

Tôi có thể sử dụng tín hiệu như trong @ alk fine answer (cũng được thảo luận here) .

Hoặc, bạn có thể sử dụng ghép kênh I/O.

Khi khởi tạo, hãy tạo đường ống chung (2). Khi đến lúc kết thúc chương trình, đóng đầu ghi của ống - bây giờ đầu đọc sẽ ngay lập tức chọn (2)/poll (2) có thể đọc được (đối với EOF). Trong khi đó, có các chủ đề bị chặn lại của bạn bao gồm đầu đọc của ống này cùng với các ổ cắm của chúng, ví dụ, một cuộc gọi chọn (2) chặn vô thời hạn.Nếu đầu đọc của đường ống trả về có thể đọc được, các luồng I/O biết đó là thời gian để chấm dứt một cách duyên dáng. Điều quan trọng nhất của kỹ thuật này là đảm bảo rằng đầu ghi chỉ được đóng một lần, như một phần tiếp theo, ngây thơ gần (2) có thể nip một số tập tin vô tội xảy ra đã được đưa ra cùng một bộ mô tả như cũ của đường ống viết.

3

Hoặc:

  1. cài đặt thời hạn đọc, với setsockopt()SO_RCVTIMEO, và bất cứ khi nào nó gây nên kiểm tra một biến trạng thái để xem nếu bạn đã nói với chính mình để dừng đọc.
  2. Nếu bạn muốn ngừng đọc ổ cắm mãi mãi, hãy tắt nó cho đầu vào với shutdown(sd, SHUT_RD). Điều này sẽ gây ra recv() để trả về số không từ bây giờ trở đi.
  3. Đặt ổ cắm thành chế độ không chặn và sử dụng select() với thời gian chờ để cho bạn biết thời điểm đọc, áp dụng cùng một chiến lược với biến trạng thái tại (1) ở trên. Tuy nhiên chế độ không chặn giới thiệu các biến chứng đáng kể trong hoạt động gửi, vì vậy bạn nên chọn (1) hoặc (2) ở trên.

Mã giả của bạn thiếu EOS- và kiểm tra lỗi. Tôi hy vọng nó không thực sự trông như thế.

Các vấn đề liên quan