如何每1 ms发送一次UDP数据包?

Xia*_*Xia 6 c sockets linux udp

我需要编写一个定期发送UDP数据包的Linux应用程序.理想情况下,频率应为每1 ms,数据包之间的间隔应保持一致.

我试过这样通过普通的套接字来做到这一点:

while(counter < 4294967295)
{
    for (k=0; k<4; k++) //Convert counter value to string
    {
        buf[k]=((unsigned char*)(&counter))[k];
    }
    sn = sendto(sender, &buf, sizeof(buf), 0, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); // Sending UDP segment
    if (sn < 0 ) error("UDP send fail!"); //Error handle
    counter++;

    nanosleep(&delay, NULL); //Sleep
}
Run Code Online (Sandbox Code Playgroud)

在上面的应用程序中,我只用一个计数器值填充UDP数据包,所以我可以在接收器的末端区分它们.

基本上这个代码确实起到了作用,但是存在以下问题:1.频率不够高,受主机性能和其他应用的影响很大.2.数据包间隔不一致,因为有RTC用作参考.但是,如果我尝试进行RTC检查,那使得数据包速率更慢.

我认为应该以更优雅的方式用不同的方法实现我的目标.请提出建议.

Chr*_*odd 5

在 Linux(或任何 UNIX)上获得重复的定期心跳之类的标准方法是使用 setitimer(2):

#include <sys/time.h>
#include <signal.h>

struct itimerval timer;
timer.it_interval.tv_sec = timer.it_value.tv_sec = 0;
timer.it_interval.tv_usec = timer.it_value.tv_usec = 1000;   /* 1000 microseconds */
if (setitimer(ITIMER_REAL, &timer, 0) < 0) {
    perror("setitimer");
    exit(1); }
Run Code Online (Sandbox Code Playgroud)

现在,您将每毫秒收到一个 SIGALRM 信号,因此您可以通过调用运行循环sigwait

sigset_t alarm_sig;
int signum;
sigemptyset(&alarm_sig);
sigaddset(&alarm_sig, SIGALRM);
while (1) {
    sigwait(&alarm_sig, &signum); /* wait until the next signal */
    ... do your stuff to send a packet, or whatever.
}
Run Code Online (Sandbox Code Playgroud)

现在,只要系统能够跟上,您就会每毫秒发送一个数据包。后者很重要——如果系统负载过重(或者创建数据包的代码太慢),下一个信号将在下一次sigwait调用之前进入并终止您的进程。如果您不想这样做,请为 SIGALARM 信号添加一个信号处理程序:

void missed_alarm(int signum) {
    /* we missed a timer signal, so won't be sending packets fast enough!!! */
    write(2, "Missed Alarm!\n", 14); /* can't use printf in a signal handler */
}

signal(SIGALRM, missed_alarm);
Run Code Online (Sandbox Code Playgroud)

现在,如果错过了警报,您的数据包发送速度将会减慢(您将错过一个插槽),但您会在 stderr 上收到有关它的消息。


上述的一个重要问题是它取决于您的系统计时器分辨率。在 Linux 上,这在很大程度上取决于内核配置 CONFIG_HIGH_RES_TIMERS。如果未启用该功能,您可能只有 10 毫秒的分辨率,因此尝试使用 1 毫秒的时钟将会严重失败。我相信大多数 x86_64 发行版上默认启用它,但您可以通过查看 /proc/timer_list 进行检查,看看“ktime_get_real”时钟的分辨率是多少。