linux非阻塞的socket EAGAIN的錯誤處理【轉】


轉自:http://blog.csdn.net/tianmohust/article/details/8691644

版權聲明:本文為博主原創文章,未經博主允許不得轉載。
在Linux中使用非阻塞的socket的情形下。

(一)發送時

  當客戶通過Socket提供的send函數發送大的數據包時,就可能返回一個EAGAIN的錯誤。該錯誤產生的原因是由於send 函數中的size變量大小超過了tcp_sendspace的值。tcp_sendspace定義了應用在調用send之前能夠在kernel中緩存的數據量。當應用程序在socket中設置了O_NDELAY或者O_NONBLOCK屬性后,如果發送緩存被占滿,send就會返回EAGAIN的錯誤。 

  為了消除該錯誤,有三種方法可以選擇: 
  1.調大tcp_sendspace,使之大於send中的size參數 
  ---no -p -o tcp_sendspace=65536 

  2.在調用send前,在setsockopt函數中為SNDBUF設置更大的值 

  3.使用write替代send,因為write沒有設置O_NDELAY或者O_NONBLOCK

(二)接收時

       接收數據時常遇到Resource temporarily unavailable的提示,errno代碼為11(EAGAIN)。這表明你在非阻塞模式下調用了阻塞操作,在該操作沒有完成就返回這個錯誤,這個錯誤不會破壞socket的同步,不用管它,下次循環接着recv就可以。對非阻塞socket而言,EAGAIN不是一種錯誤。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。其實這算不上錯誤,只是一種異常而已。

  另外,如果出現EINTR即errno為4,錯誤描述Interrupted system call,操作也應該繼續。

  最后,如果recv的返回值為0,那表明對方已將連接斷開,我們的接收操作也應該結束。

(三)以下是另一種解釋

假如發送端流量大於接收端的流量(意思是epoll所在的程序讀比轉發的socket要快),由於是非阻塞的socket,那么send()函數雖然返回,但實際緩沖區的數據並未真正發給接收端,這樣不斷的讀和發,當緩沖區滿后會產生EAGAIN錯誤(參考man send),同時,不理會這次請求發送的數據.所以,
        需要封裝socket_send()的函數用來處理這種情況,該函數會盡量將數據寫完再返回,返回-1表示出錯。在socket_send()內部,當寫緩沖已滿(send()返回-1,且errno為EAGAIN),那么會等待后再重試.這種方式並不很完美,在理論上可能會長時間的阻塞在socket_send()內部,但暫沒有更好的辦法.
這種方法類似於readn和writen的封裝(自己寫過,在《UNIX環境高級編程》中也有介紹)

[cpp] view plain copy

    size_t socket_send(int sockfd, const char* buffer, size_t buflen)  
    {  
        size_t tmp;  
        size_t total = buflen;  
        const char *p = buffer;  
      
        while(1)  
        {  
            tmp = send(sockfd, p, total, 0);  
      
            if(tmp < 0)  
            {  
                // 當send收到信號時,可以繼續寫,但這里返回-1.  
                if(errno == EINTR)  
                {  
                    return -1;  
                }  
      
                // 當socket是非阻塞時,如返回此錯誤,表示寫緩沖隊列已滿,  
                // 在這里做延時后再重試.  
                if(errno == EAGAIN)  
                {  
                    usleep(1000);  
                    continue;  
                }  
      
                return -1;  
            }  
      
            if((size_t)tmp == total)  
            {  
                return buflen;  
            }  
      
            total -= tmp;  
            p += tmp;  
        }  
      
        return tmp;  
    }  
       

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM