Linux網絡編程 -- select/epoll得知socket有數據可讀,如何判斷數據所有被讀取完畢?

http://blog.csdn.net/ldd909/article/details/6168077網絡

 

補充一點:只有在使用epoll ET(Edge Trigger)模式的時候,才須要關注數據是否讀取完畢了。使用select或者epoll的LT模式,其實根本不用關注數據是否讀完了,select/epoll檢測到有數據可讀去讀就OK了。socket

 

這裏有兩種作法:函數

 

1. 針對TCP,調用recv方法,根據recv方法的返回值,若是返回值小於咱們指定的recv buffer的大小,則認爲數據已經所有接收完成。在Linux epoll的manual中,也有相似的描述:測試

 

For stream-oriented files (e.g., pipe, FIFO, stream socket), the condition that the read/write I/O space is exhausted can also be detected  by  checking the  amount  of data read from / written to the target file descriptor.  For example, if you call read(2) by asking to read a certain amount of data and read(2) returns a lower number of bytes, you can be sure of having exhausted the read I/O space for the file descriptor.  The same is true when  writing using write(2).  (Avoid this latter technique if you cannot guarantee that the monitored file descriptor always refers to a stream-oriented file.)ui

 

2. TCP和UDP都適用。將socket設成NONBLOCK(使用fcntl函數),而後select到該socket可讀以後,使用read/recv來讀取數據。當函數返回-1,同時errno是EAGAIN或EWOULDBLOCK的時候,表示數據已經所有讀取完畢。this

 

實驗結論:spa

 

第一種方法是錯誤的。簡單來講,若是發送了4K字節,recv的時候使用一個2K的buffer,那麼,recv兩次以後就再也沒有數據能夠recv了,此時recv就會block。永遠不會出現recv返回值小於2K的狀況(注:recv/read返回0表示對端socket已經關閉)。.net

 

因此推薦使用第二種方法,第二種方法正確並且對TCP和UDP都管用。事實上,不論什麼平臺編寫網絡程序,我認爲都應該使用select+NONBLOCK socket的方式。這樣能夠保證你的程序至少不會在recv/send/accept/connect這些操做上發生block從而將整個網絡服務都停下來。很差的地方就是不太利於Debug,若是是block的socket,那麼GDB一跟就能知道阻塞在什麼地方了。。。blog

 

其實所謂讀取完畢指的是kernel中該socket對應的input data queue中的數據所有被讀取了出來,從而該socket在kernel中被設置成了unreadable的狀態。因此若是好比在局域網內,sender一直不斷髮送數據,則select到recv socket可讀以後,咱們就能夠一直不停的讀取到數據。因此,若是一個網絡程序接收端想一次把數據所有接收完而且將全部接收到的數據都保存在內存中的話,就須要考慮到這種狀況,避免佔用過多的內存。ip

 

下面是測試代碼,代碼中client讀取了4K了以後就退出了,由於sender每次發送4K,因此client select到一次readable以後,就只會讀取到4K。

Client.c:

#include  < stdio.h > 
#include  < stdlib.h > 
#include  < errno.h > 
#include  < string .h > 
#include  < netdb.h > 
#include  < sys / types.h > 
#include  < netinet / in .h > 
#include  < sys / socket.h > 
#include  < fcntl.h > 
#include  < unistd.h > 
#include  < sys / select.h > 

#define  SERVPORT 3333 
#define  RECV_BUF_SIZE 1024 

void  setnonblocking( int  sock)
{
     int  opts;
    opts = fcntl(sock,F_GETFL);
     if (opts < 0 )
    {
        perror( " fcntl(sock,GETFL) " );
        exit( 1 );
    }
    opts  =  opts | O_NONBLOCK;
     if (fcntl(sock,F_SETFL,opts) < 0 )
    {
        perror( " fcntl(sock,SETFL,opts) " );
        exit( 1 );
    }
}

int  main( int  argc,  char   * argv[])
{
     int  sockfd, iResult;
     char  buf[RECV_BUF_SIZE];
     struct  sockaddr_in serv_addr;
    fd_set readset, testset;

    sockfd  =  socket(AF_INET, SOCK_STREAM,  0 );
    setnonblocking(sockfd);

    memset( & serv_addr,  0 ,  sizeof (serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERVPORT);
    serv_addr.sin_addr.s_addr  =  inet_addr( " 127.0.0.1 " );

    connect(sockfd, ( struct  sockaddr  * ) & serv_addr,  sizeof (serv_addr));

    FD_ZERO( & readset);
    FD_SET(sockfd,  & readset);

    testset  =  readset;
    iResult  =  select(sockfd  +   1 ,  & testset, NULL, NULL, NULL);

     while  ( 1 ) {
        iResult  =  recv(sockfd, buf, RECV_BUF_SIZE,  0 );
         if  (iResult  ==   - 1 ) {
             if  (errno  ==  EAGAIN  ||  errno  ==  EWOULDBLOCK) {
                printf( " recv finish detected, quit.../n " );
                 break ;
            }
        }
        printf( " Received %d bytes/n " , iResult);
    }

    printf( " Final iResult: %d/n " , iResult);
     return   0 ;
}

 

 

Server.c:

#include  < stdio.h >  #include  < stdlib.h >  #include  < errno.h >  #include  < string .h >  #include  < sys / types.h >  #include  < netinet / in .h >  #include  < sys / socket.h >  #include  < sys / wait.h >  #define  SERVPORT 3333  #define  BACKLOG 10  #define  SEND_BUF_SIZE 4096  int  main( int  argc,  char   * argv[]) {      int  sockfd, client_fd, i;      struct  sockaddr_in my_addr;      char   * buffer  =  NULL;     sockfd  =  socket(AF_INET, SOCK_STREAM,  0 );     memset( & my_addr,  0 ,  sizeof (my_addr));     my_addr.sin_family = AF_INET;     my_addr.sin_port = htons(SERVPORT);     my_addr.sin_addr.s_addr  =  inet_addr( " 127.0.0.1 " );     bind(sockfd, ( struct  sockaddr  * ) & my_addr,  sizeof ( struct  sockaddr));     listen(sockfd, BACKLOG);     client_fd  =  accept(sockfd, NULL, NULL);     buffer  =  malloc(SEND_BUF_SIZE);       for  (i  =   0 ; i  <   100 ; i ++ ) {         send(client_fd, buffer, SEND_BUF_SIZE,  0 );         sleep( 1 );     }     sleep( 10 );     close(client_fd);     close(sockfd);     free(buffer);      return   0 ; }

相關文章
相關標籤/搜索