gpt4 book ai didi

linux - 当 recv buf 已经有数据时,tcp socket recv 会返回 "resource temporarily unavailable"

转载 作者:太空宇宙 更新时间:2023-11-04 12:20:42 27 4
gpt4 key购买 nike

我使用 epoll 实现 TCP 套接字通信来监视所有客户端事件,只有一个线程在一个 for 循环中处理所有客户端。每个套接字都是非阻塞的。

现在我遇到了一个问题,当客户端发送的数据超过MTU时,意味着有几个碎片数据包,服务器总是无法完整读取所有数据。如下图,我先读取头部,从头部获取pdu len,然后读取pdu部分。

问题是虽然我成功读取了头部,紧随 pdu recv(),但它总是返回 EAGAIN 几次。所以我的重试会中断。因为服务器需要处理成千上万的客户端事件,所以我认为让重试一直持续下去是一个很大的性能消耗,这是不能容忍的。

我用tcpdump从客户端抓包,每个包都是fragment to max 1448 bytes data,但是head只有5 bytes,为什么我可以成功读取head,但是后面的数据recv()操作会返回EAGAIN?当数据已经到达 recv 缓冲区时,recv 是否有可能返回 EAGAIN?在我看来,我可以读取前 5 个字节,因此必须在 recv 缓冲区中读取更多数据。

可能与tcp/ip栈中的assemble过程有关。代码如下,每个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(如果是非阻塞套接字)。

一个常见的模式是:

  1. 使用select/poll/epoll 检测何时可以读取套接字。
  2. 在准备就绪的套接字上调用一次recv,并将接收到的数据附加到缓冲区。
  3. 检查缓冲区是否包含足够的数据进行处理。如果是,则处理。否则让 select/poll/epoll 告诉你什么时候可以阅读更多内容。

关于linux - 当 recv buf 已经有数据时,tcp socket recv 会返回 "resource temporarily unavailable",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46099737/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com