数据在广播 UDP 套接字中累积(FIFO?)

Data accumulates (FIFO?) in broadcast UDP socket

我有一个设备每秒向我发送一个监控数据流(大小为 58),广播。我正在用 C 程序阅读这个流。 我的问题是,如果我几秒钟不读这个流,下次我读它时,我会得到更多我需要的数据,就像我有一个 FIFO 在我不读的时候一直在填充流。

我尝试使用不同的读取方法,例如使用 select、非阻塞套接字……总是一样。也许我只是想念一个细节...... 这是一个示例代码:

#include <stdio.h>      /* for printf() and fprintf() */
#include <sys/socket.h> /* for socket(), connect(), sendto(), and recvfrom() */
#include <arpa/inet.h>  /* for sockaddr_in and inet_addr() */
#include <stdlib.h>     /* for atoi() and exit() */
#include <string.h>     /* for memset() */
#include <unistd.h>     /* for close() */

#define MAXRECVSTRING 58  /* Longest string to receive */


int main(int argc, char *argv[])
{
    int sock;                         /* Socket */
    struct sockaddr_in broadcastAddr; /* Broadcast Address */
    unsigned short broadcastPort;     /* Port */
    char recvString[MAXRECVSTRING+1]; /* Buffer for received string */
    int recvStringLen;                /* Length of received string */

    if (argc != 2)    /* Test for correct number of arguments */
    {
        fprintf(stderr,"Usage: %s <Broadcast Port>\n", argv[0]);
        exit(1);
    }

    broadcastPort = atoi(argv[1]);   /* First arg: broadcast port */

    /* Create a best-effort datagram socket using UDP */
    if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
        perror("socket() failed");

    /* Construct bind structure */
    memset(&broadcastAddr, 0, sizeof(broadcastAddr));   /* Zero out structure */
    broadcastAddr.sin_family = AF_INET;                 /* Internet address family */
    broadcastAddr.sin_addr.s_addr = htonl(INADDR_ANY);  /* Any incoming interface */
    broadcastAddr.sin_port = htons(broadcastPort);      /* Broadcast port */

    /* Bind to the broadcast port */
    if (bind(sock, (struct sockaddr *) &broadcastAddr, sizeof(broadcastAddr)) < 0)
        perror("bind() failed");

    /* Receive datagram from the server */
    int g=0;
    int time_to_sleep=10; 
    while(1)
    {
        if(g==10)
        {
            while(time_to_sleep)
            {
                printf("sleep for %d\n", time_to_sleep);
                time_to_sleep=sleep(time_to_sleep);
            }//after that my next read is 10 data stream of 58 in one !!
        }


        if ((recvStringLen = recvfrom(sock, recvString, MAXRECVSTRING, 0, NULL, 0)) < 0)
        {
             perror("recvfrom() failed");
        }
        //later will check the header but for now just the size is enough
        if (recvStringLen==58){ printf("Read okay bc Size = %d \n", recvStringLen); }


    }

    close(sock);
    exit(0);
}

所以我将尝试解释我的问题: 我的设备每秒钟向我发送 UDP 数据包(大小 58),我一直在连续阅读。 x 时间后 (g==10) 我决定在 10 秒内睡觉。在这 10 秒内,我的设备一直在发送 UDP 数据包,但我不读取它们,也不想读取它们。在第 11 秒,当我醒来时,我想读取我的 (g==10)+11 个数据包,而不是第 11 个 + 我在睡觉时没有读取的第 10 个数据包。 不幸的是,当我用一个 recvfrom 阅读第 11 个时,我得到了所有前 10 个 ...

我试过使用和不使用以太网交换机以防万一,但同样的问题。我一定是误解了套接字中的某些东西......你能帮我吗?

顺便说一句,我的代码可能是纯粹的灾难,我是初学者。不要犹豫,纠正我!

谢谢!

您实际上并没有从单个 recvfrom 中获得 10 或 11 个数据包。您在循环中调用 recvfrom,循环的每次迭代都会读取一个数据包。所以你需要一些方法来知道你最近读取的数据包是 "last" 一个。

这样做的方法是不断地看,直到没有什么可读的,那么你就知道最近的就是最后一个了。但是,您的代码当前编写的方式是,如果没有要读取的数据包,那么 recvfrom 将阻塞,直到有数据包可用为止。您可以通过将套接字置于非阻塞模式来更改它:

int fd_flag;
if ((fdflag = fcntl(sock, F_GETFL)) == -1) {
    perror("Error getting socket descriptor flags");
    exit(1);
}
fdflag |= O_NONBLOCK;
if (fcntl(sock, F_SETFL, fdflag) == -1) {
    perror("Error setting non-blocking option");
    exit(1); 
}

现在,当您调用 recvfrom 时,如果没有可用数据,它将 return -1 并将 errno 设置为 EAGAIN。您可以使用它来检查 "last" 数据包:

while(1)
{
    while(time_to_sleep)
    {
        printf("sleep for %d\n", time_to_sleep);
        time_to_sleep=sleep(time_to_sleep);
    }//after that my next read is 10 data stream of 58 in one !!

    int read_one = 0;
    while (1) {
        if ((recvStringLen = recvfrom(sock, recvString, MAXRECVSTRING, 0, NULL, 0)) < 0)
        {
            if (errno == EAGAIN) {
                // nothing more to read
                break;
            } else {
                perror("recvfrom() failed");
            }
        } else {
            read_one = 1;
            if (recvStringLen==58){ printf("Read okay bc Size = %d \n", recvStringLen); }
        }
    }

    if (read_one) {
        // process last packet read
    } else {
        // nothing read, so do nothing
    }
}

I must misunderstand something in socket ... can you help me ?

内核中有接收和发送套接字缓冲区。内核为您的侦听套接字接收数据报并将它们存储在内核套接字接收缓冲区中。 recvfrom 调用将最旧的数据报从内核套接字缓冲区复制到您的 user-space 缓冲区中。您不调用 recvfrom 的事实并不意味着内核停止接收数据报(您需要关闭套接字以使内核停止接收数据)。