我试图在 C 中实现一个发送/接收原始以太网帧的协议(protocol),但我在使用 poll()
时遇到了一些问题。和 recvfrom()
在 Linux 环境中。我认为我的问题主要是概念性的,所以我暂时不会发布我的代码。
我有两个用于传入数据的套接字,以及poll()
用于通过其返回值和关联的状态指示任一套接字上的数据何时准备就绪 pollfd
结构体。当我第一次运行程序时,这对我有用。为了测试,我只对 ARP 帧感兴趣,我可以使用 poll()
等待 ARP 帧到达。当它到达时,我调用 recvfrom()
将数据复制到我可以处理它的位置。所有这些都工作正常。
问题是后续调用 poll()
继续报告数据已准备好从套接字读取,即使没有新数据到达套接字。一旦我调用recvfrom()
并从套接字中读取数据,我想要 poll()
等待新帧到达,然后再报告数据已准备就绪。我从未使用过 poll()
之前,所以我不确定是否需要一个明确的步骤来“清除”描述符,以便它停止导致 poll()
报告数据准备就绪,直到出现新帧。我正在清除 revents
pollfd
的成员调用前的结构 poll()
, 但每次调用 poll()
正在设置 revents
的值回到 1。
我已经查看了 poll()
手册页,我没有找到这方面的信息。我觉得我误解了套接字/轮询在高层次上的工作方式,因此我们将不胜感激。
[编辑] 这是我的大部分代码。实际上,我已经通过只包含一个套接字来简化了这个示例的代码。所有这些也封装在类中并散布开来,但是像这样重写它会重现我的问题。我的最终代码对 API 调用进行了错误检查,并复制了我的接收缓冲区以处理帧,而不仅仅是打印内容,但除此之外它几乎是一样的。
#include <stdio.h>
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <poll.h>
#include <linux/if_ether.h>
int main()
{
uint8_t sendBuffer_[1536];
uint8_t receiveBuffer_[1536];
struct pollfd pollStruct[1];
printf("Making socket...");
int mySocket = socket( AF_PACKET, SOCK_RAW, htons(ETH_P_ARP) );
fcntl(mySocket, F_SETFL, O_NONBLOCK);
char *options;
options = "eth0";
setsockopt(mySocket, SOL_SOCKET, SO_BINDTODEVICE, options, 4);
printf("Descriptor: %i\n", mySocket);
while(1)
{
pollStruct[0].fd = mySocket;
pollStruct[0].events = POLLIN;
pollStruct[0].revents = 0;
if( poll(pollStruct, 1, 0) == 1)
{
printf("New Frame Arrived: ");
uint16_t frameLength = recvfrom( mySocket, receiveBuffer_, 1536, 0, NULL, NULL);
printf("Destination MAC: ");
for ( uint8_t i = 0; i < 6; ++i ) {
printf("0x%x ", receiveBuffer_[i]);
}
printf("\nSource MAC: ");
for ( uint8_t i = 0; i < 6; ++i ) {
printf("0x%x ", receiveBuffer_[i + 6]);
}
printf("\nEtherType: ");
for ( uint8_t i = 0; i < 2; ++i ) {
printf("0x%x ", receiveBuffer_[i + 12]);
}
printf("\nPayload: ");
for ( uint32_t i = 14; i < frameLength; ++i ) {
printf("0x%x ", receiveBuffer_[i]);
}
printf("\n");
//Clear buffer
for ( uint32_t i = 0; i < frameLength; ++i )
receiveBuffer_[i] = 0;
}
}
}
我最初没有提到这一点,但我正在尝试将这段代码部署到 Beaglebone Black 上。当我在使用 GCC 编译的 Ubuntu 的虚拟机中运行这个程序时,它按我预期的那样工作。接收/打印帧后,程序会在视觉上空闲,直到新帧到达。当我交叉编译程序并运行它时,程序会连续打印相同的数据包,直到有新的数据包到达。因为我正在清理 receiverBuffer[]
每当recvfrom()
返回,recvfrom()
一旦将数据复制到我的缓冲区中,它似乎并没有从其内部缓冲区中清除数据,因为它一直在复制数据。
最佳答案
我觉得可能是以下原因 * 不要在 poll 中设置 revents。它实际上是一个输出参数 * 在超时中使用一些正值。如果你想要无限等待时间使用-1。引用轮询手册页
关于对 poll() 和 recvfrom() 的混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21742261/