2

我正在尝试在 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()清除,因此一旦将数据复制到我的缓冲区中,似乎就不会从其内部缓冲区中清除数据,因为它会不断地复制数据。

4

1 回答 1

1

我认为以下可能是一个原因 * 不要在 poll 中设置 revents。它实际上是一个输出参数 * 在超时时使用一些正值。如果您想要无限等待时间,请使用-1。请参阅民意调查手册页

于 2014-07-30T06:29:57.407 回答