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 ; }