2009-04-09 46 views
6

Một trong các dự án của tôi trên Linux sử dụng chặn ổ cắm. Mọi thứ diễn ra rất nghiêm túc nên việc không chặn sẽ khiến mọi thứ trở nên phức tạp hơn. Dù sao, tôi thấy rằng thường có một cuộc gọi recv() đang trả lại -1 với errno được đặt thành EAGAIN.Trả về socket chặn EAGAIN

Trang man chỉ thực sự đề cập đến điều này xảy ra đối với các ổ cắm không chặn, có ý nghĩa. Với không bị chặn, ổ cắm có thể có hoặc không có sẵn để bạn có thể cần phải thử lại.

Điều gì sẽ xảy ra cho ổ cắm chặn? Tôi có thể làm gì để tránh nó không?

Tại thời điểm này, mã của tôi để đối phó với nó trông giống như thế này (Tôi có nó ném một ngoại lệ về lỗi, nhưng ngoài ra nó là một wrapper rất đơn giản xung quanh recv()):

int ret; 
do { 
    ret = ::recv(socket, buf, len, flags | MSG_NOSIGNAL); 
} while(ret == -1 && errno == EAGAIN); 


if(ret == -1) { 
    throw socket_error(strerror(errno)); 
} 
return ret; 

Điều này có đúng không? Tình trạng EAGAIN bị ảnh hưởng khá thường xuyên.

EDIT: một số điều tôi đã nhận thấy có thể có liên quan.

  1. Tôi đặt thời gian chờ đọc trên ổ cắm bằng cách sử dụng setsockopts() nhưng được đặt thành 30 giây. cách thực hiện của EAGAIN thường xuyên hơn 30 giây một lần. CORRECTION gỡ lỗi của tôi bị lỗi, EAGAIN không xảy ra thường xuyên như tôi nghĩ họ đã làm. Có lẽ nó là thời gian chờ kích hoạt.

  2. Để kết nối, tôi muốn có thể kết nối thời gian chờ, vì vậy tôi tạm thời đặt ổ cắm thành không bị chặn. mã mà trông như thế này:

    int  error = 0; 
    fd_set rset; 
    fd_set wset; 
    int  n; 
    const SOCKET sock = m_Socket; 
    
    // set the socket as nonblocking IO 
    const int flags = fcntl (sock, F_GETFL, 0); 
    fcntl(sock, F_SETFL, flags | O_NONBLOCK); 
    
    errno = 0; 
    
    // we connect, but it will return soon 
    n = ::connect(sock, addr, size_addr); 
    
    if(n < 0) { 
        if (errno != EINPROGRESS) { 
         return -1; 
        } 
    } else if (n == 0) { 
        goto done; 
    } 
    
    FD_ZERO(&rset); 
    FD_ZERO(&wset); 
    FD_SET(sock, &rset); 
    FD_SET(sock, &wset); 
    
    struct timeval tval; 
    tval.tv_sec = timeout; 
    tval.tv_usec = 0; 
    
    // We "select()" until connect() returns its result or timeout 
    n = select(sock + 1, &rset, &wset, 0, timeout ? &tval : 0); 
    if(n == 0) {  
        errno = ETIMEDOUT; 
        return -1; 
    } 
    
    if (FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset)) { 
        socklen_t len = sizeof(error); 
        if (getsockopt(SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 
         return -1; 
        } 
    } else { 
        return -1; 
    } 
    
    done: 
    // We change the socket options back to blocking IO 
    if (fcntl(sock, F_SETFL, flags) == -1) { 
        return -1; 
    } 
    return 0; 
    

Ý tưởng là tôi đặt nó là non-blocking, cố gắng một kết nối và chọn trên ổ cắm vì vậy tôi có thể thi hành một thời gian chờ. Cả hai thiết lập và khôi phục lại fcntl() cuộc gọi trở lại thành công, do đó, các ổ cắm sẽ kết thúc trong chế độ chặn một lần nữa khi chức năng này hoàn thành.

Trả lời

19

Có thể là bạn có một khác không nhận được thời gian chờ đặt trên ổ cắm (thông qua setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...)) như đó cũng sẽ gây recv trở EAGAIN

+0

có, nhưng nó được đặt thành 30000 mili giây, tôi nhận được * cách EAGAIN * thường xuyên hơn thế. Khá nhiều constatnly. –

+0

* SỬA ĐỔI * gỡ lỗi của tôi đã bị thiếu sót, EAGAIN không xảy ra thường xuyên như tôi nghĩ họ đã làm. Có lẽ nó là thời gian chờ kích hoạt. –

1

Có thể bạn đang sử dụng MSG_DONTWAIT đang được chỉ định làm một phần của cờ của mình? Trang man cho biết EAGAIN sẽ xảy ra nếu không có sẵn dữ liệu và cờ này được chỉ định.

Nếu bạn thực sự muốn chặn một khối cho đến khi recv() có phần thành công, bạn có thể muốn sử dụng cờ MSG_WAITALL.

+0

Tôi chỉ cần grepped cây nguồn của tôi, MSG_DONTWAIT không được sử dụng. –

0

Tôi không đề xuất đây là bản sửa lỗi đầu tiên, nhưng nếu bạn không còn tùy chọn, bạn luôn có thể select() trên ổ cắm với thời gian chờ hợp lý dài để buộc phải đợi dữ liệu.

0

EAGAIN được tạo ra bởi hệ điều hành gần giống như một "Oops! Tôi xin lỗi để làm phiền bạn.". Trong trường hợp lỗi này, bạn có thể thử đọc lại, Đây không phải là lỗi nghiêm trọng hoặc gây tử vong.Tôi đã thấy những gián đoạn xảy ra trong Linux và LynxOS bất cứ nơi nào từ một ngày đến 100 lần một ngày.

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