在 C/C++ 中调用 recvfrom() 函数时的读取逻辑是什么

What's the read logic when I call recvfrom() function in C/C++

我写了一个 C++ 程序来创建一个套接字并绑定到这个套接字上以接收 ICMP/UDP 数据包。我写的代码如下:

while(true){
   recvfrom(sockId, rePack, sizeof(rePack), 0, (struct sockaddr *)&raddr, (socklen_t *)&len);
   processPakcet(recv_size);
}

于是,我使用了一个无限的while循环来不断地接收消息,但是我担心以下两个问题:

1, How long the message would be kept in the receiver queue or say in NIC queue?

我担心如果处理第一条消息的时间太长,那么我可能会错过第二条消息。那么读完之后应该多快读呢

2, How to prevent reading the duplicated messages?

即,接收者队列是否认识我,当我的线程读取完成的第一条消息时,队列会自动给我第二条消息吗?或者说,当我读到第一条消息时,第一条消息就会被队列删除,再也没有人能收到。

另外,我觉得while(true) 模块不好,请大家给我一个好的建议。 (听说轮询模块之类的)

首先,您应该始终检查 recvfrom 中的 return 值。 recvfrom 不太可能会失败,但如果它失败了(例如,如果您稍后实现信号处理,它可能会因 EINTR 而失败),您将处理未定义的数据。另外,当然,return 值会告诉您收到的数据包的大小。

对于问题 1,实际答案取决于操作系统。但是,大多数操作系统会为您缓冲一定数量的数据包。处理传入数据包的 OS 中断处理程序永远不会将其直接复制到您的应用程序级缓冲区中,因此它总是首先进入 OS 缓冲区。 OS 之前已经注意到您对它的兴趣(通过创建套接字并绑定它表示您感兴趣),因此它将指向缓冲区的指针放置到与您的套接字关联的队列上。

OS 代码的不同部分将(在中断处理程序完成后)将数据从 OS 缓冲区复制到您的应用程序内存中,释放 OS 缓冲区, 和 return 从 recvfrom 系统调用到你的程序。如果有其他数据包进来,无论是在您开始处理第一个数据包之前还是之后,它们也会被放入队列中。

那个队列当然不是无限的。您可能可以在系统范围内配置可以保留多少数据包(或多少缓冲区 space)(想想 linux 中的 sysctl 类型设置),或者在单个套接字级别 (setsockopt / ioctl).

如果在您调用 recvfrom 时,套接字上已经有排队的数据包,系统调用处理程序将不会 阻止 您的进程,而只会简单地复制从下一个排队数据包的OS缓冲区进入你的缓冲区,释放OS缓冲区,并立即return。只要您处理传入数据包的速度与它们到达的速度大致相同或更快,您就不会丢失任何数据包。 (但是,请注意,如果另一个系统正在以非常高的速率生成数据包,很可能 OS 保留的内存 在某个时候耗尽,之后 OS 将简单地丢弃超出其资源预留的数据包。)

对于问题 2,您不会收到重复的消息(除非您机器上游的某些东西实际上在复制它们)。一旦排队的消息被复制到您的缓冲区中,它就会在 return 发送给您之前被释放。该消息已永远消失。

(请注意,其他一些进程也可能创建了一个套接字,表示对相同的数据包感兴趣。该进程也会获得数据包数据的副本,这通常在操作系统内部通过引用计数进行处理而不是通过实际复制 OS 缓冲区,尽管该细节对应用程序是不可见的。无论如何,一旦所有感兴趣的进程都收到了数据包,它就会被丢弃。)

while (true) 循环真的没有任何问题;对于长 运行 服务器类型的程序来说,这是一种非常常见的控制结构。如果您的程序在此期间没有其他需要做的事情,while true 允许它在 recvfrom 中阻塞是实现它的最简单也是最清晰的方法。

(您可以使用 select(2)poll(2) 调用来等待。这允许您处理同时等待多个文件描述符中的任何一个,或定期“超时”然后去做别的事情,比如说,但是如果你没有别的事情你可能需要同时做,那就是引入不必要的复杂化。)