我使用epoll监视所有客户端事件来实现TCP套接字通信,只有一个线程在for循环中处理所有客户端。每个套接字都是非阻塞的。
现在我只是遇到一个问题,当客户端发送的数据超过MTU时,意味着有多个分片数据包,服务器始终无法完全读取所有数据。
像下面一样,我先阅读磁头,从磁头获得pdu len,然后阅读pdu部分。
问题是尽管我成功地读取了磁头,紧跟着pdu recv(),但是它总是多次返回EAGAIN。所以我的重试会中断。
因为服务器需要处理数千个客户端事件,所以我认为让重试始终继续是一个很大的性能消耗,这是不能容忍的。
我使用tcpdump捕获来自客户端的数据包,每个数据包都是最大1448字节数据的片段,但是head只有5个字节,为什么我可以成功读取head,但是以下数据recv()操作将返回EAGAIN?当数据已经到达接收缓冲区时,接收返回是否可能返回EAGAIN?我认为,我可以读取前5个字节,因此必须在recv缓冲区中读取更多数据。
可能与tcp/ip堆栈中的汇编过程有关。
代码如下所示,每个pdu recv,需要10次或更多次重试,也许成功。
...
#define HDR_LEN 5
n = epoll(epfd, events, 1000, -1)
for(i =0; i < n; i++)
{
uint8 pHdr[HDR_LEN] = {0};
uint16 pdulen = 0, offset =0;
infd = events[i].fd;
nRead = recv(infd, pHdr, HDR_LEN); // read the data head first
pdulen = ntohs(*(uint16 *)(pHdr+2)); // get the pdu len from the head
uint8 *pbuf = malloc(pdulen+HDR_LEN);
memcpy(pbuf, pHdr, HDR_LEN); // move the head to buf
while(offset != pdulen) // then read the pdu data
{
nRead = recv(infd, pbuf+HDR_LEN+offset, pdulen-offset);
if (nRead <=0)
{
if (nRead == -1 && errno == EAGAIN) // resource temporarily unavailable
{
if (retry < 5)
{
usleep(500);
retry++;
continue;
}
else
break; // already try 5 times, should always continue?
}
else
break;
}
else
{
offset += nRead;
retry = 0;
}
}
if (offset == pdulen)
process(pbuf, pdulen+HDR_LEN); // process the complete data
...
}
...
最佳答案
epoll
会告诉您一次是否可以不阻塞地进行recv
。如果recv
消耗了套接字中的所有数据,则下一个recv
将阻塞,直到有更多数据为止,或者返回EAGAIN(如果是非阻塞套接字)。
常见的模式是:
select/poll/epoll
检测何时可以读取套接字。 recv
,然后将接收到的数据附加到缓冲区。 select/poll/epoll
告诉您何时可以阅读更多内容。 关于linux - 当recv buf已经有数据时,tcp socket recv将返回 “resource temporarily unavailable”,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46099737/