我正在尝试在 C 中实现一个发送/接收原始以太网帧的协议,并且在 Linux 环境中使用poll()
和遇到了一些问题。recvfrom()
我认为我的问题主要是概念性的,所以我暂时避免发布我的代码。
我有两个用于传入数据的套接字,用于通过其返回值和关联结构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 的 VM 中运行该程序时,它可以按我的预期工作。在接收/打印一帧后,程序在视觉上处于空闲状态,直到新的帧到达。当我交叉编译程序并运行它时,程序会不断打印相同的数据包,直到新的数据包到达。由于我在receiverBuffer[]
每次recvfrom()
返回时都会recvfrom()
清除,因此一旦将数据复制到我的缓冲区中,似乎就不会从其内部缓冲区中清除数据,因为它会不断地复制数据。