网络编程之epoll怎么判断数据读取完毕?

只有在使用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);
}
网络编程中,可以使用epoll来实现多个客户端转发到多个服务器的功能。epoll是Linux下的一种高性能I/O事件通知机制,可以有效地处理大量的并发连接。 以下是使用epoll实现多个客户端转发到多个服务器的基本步骤: 1. 创建一个监听套接字,用于接收客户端连接。将该套接字添加到epoll事件集合中,监听读事件。 2. 创建一个epoll实例,通过epoll_create函数进行创建。 3. 将监听套接字添加到epoll实例中,使用epoll_ctl函数进行添加。 4. 进入事件循环,通过epoll_wait函数等待事件的发生。该函数会阻塞,直到有事件发生或超时。 5. 当有事件发生时,通过遍历事件数组,处理相应的事件。 6. 如果是监听套接字上有读事件发生,说明有新的客户端连接请求。使用accept函数接受连接,并将新的客户端套接字添加到epoll实例中。 7. 如果是客户端套接字上有读事件发生,说明有客户端发送数据到服务器。可以读取数据,并根据转发策略选择目标服务器,将数据转发给相应的服务器。 8. 如果是服务器套接字上有读事件发生,说明有服务器返回数据给客户端。可以读取数据,并将数据发送给相应的客户端。 9. 处理完事件后,将相应的套接字从epoll实例中删除。 10. 重复步骤4-9,直到事件循环结束。 通过以上步骤,可以实现多个客户端同时连接到服务器,并进行数据转发的功能。需要注意的是,epoll是一种边缘触发模式,需要在处理事件时确保将所有数据读取完毕或发送完毕,以免遗漏数据或造成阻塞。另外,需要根据具体的业务需求来确定转发策略和服务器选择算法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值