只有在使用epoll ET(Edge Trigger)模式的时候,才需要关注数据是否读取完毕了。使用select或者epoll的LT模式,其实根本不用关注数据是否读完了,select/epoll检测到有数据可读去读就OK了。
之前写过几篇关于网络编程的文章,c++之网络编程,c++之网络编程–文件传输,QT之TCP网络编程,QT之网络编程-文件传输,可以参考一下。
epoll怎么判断数据读取完毕,这里有两种做法:
1、针对TCP,调用recv方法,根据recv的返回值。如果返回值小于我们设定的recv buff的大小,那么就认为接收完毕。
2、TCP、UDP都适用,将socket设为NOBLOCK状态(使用fcntl函数),然后select该socket可读的时候,使用read/recv函数读取数据。当返回值为-1,并且errno是EAGAIN或EWOULDBLOCK的时候,表示数据读取完毕。
但是,第一种方法有时是错误的。简单来说,如果发送了4K字节,recv的时候使用一个2K的buffer,那么,recv两次之后就再也没有数据可以recv了,此时recv就会block。永远不会出现recv返回值小于2K的情况(注:recv/read返回0表示对端socket已经关闭)。
一、fcntl函数
详细可见Linux下socket设置为非阻塞方式和fcntl系统调用
功能描述:根据文件描述符来操作文件的特性。
用法:
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
参数:
fd:文件描述符。
cmd:操作命令。
arg:供命令使用的参数。
lock:同上。
有以下操作命令(cmd)可供使用
1) F_DUPFD :复制文件描述词 。
2) FD_CLOEXEC :设置close-on-exec标志。如果FD_CLOEXEC位是0,执行execve的过程中,文件保持打开。反之则关闭。
3) F_GETFD :读取文件描述词标志。
4) F_SETFD :设置文件描述词标志。
5) F_GETFL :读取文件状态标志。
6) F_SETFL :设置文件状态标志。
其中O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_NOCTTY 和 O_TRUNC不受影响,
可以更改的标志有 O_APPEND,O_ASYNC, O_DIRECT, O_NOATIME 和 O_NONBLOCK。
7) F_GETLK, F_SETLK 和 F_SETLKW :获取,释放或测试记录锁
用以下方法将socket设置为非阻塞方式
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
将非阻塞的设置回阻塞可以用
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags & ~O_NONBLOCK);
二、errno错误码
详细可见Linux errno详解
Linux中系统调用的错误都存储于 errno中,errno由操作系统维护,存储就近发生的错误,即下一次的错误码会覆盖掉上一次的错误。
关于errno的其中两个宏定义:
#define EAGAIN 11 /* Try again */
#define EWOULDBLOCK EAGAIN /* Operation would block */
三、实例判断数据读取完毕。
1、将socket设置成非阻塞
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 );
}
}
2、判断错误码errno
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);
}