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

时间:2014-08-15 13:43:33

标签: c linux sockets udp

我需要为Linux编写一个定期发送UDP数据包的应用程序。 理想情况下,频率应为每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
}

在上面的应用程序中,我只使用计数器值填充UDP数据包,因此我可以在接收方的末尾区分它们。

基本上这段代码完成了它的工作,但是有这些问题: 1.频率不够高,受主机性能和其他应用的影响很大。 2.数据包间隔不一致,因为有RTC用作参考。但是,如果我尝试进行RTC检查,则会使数据包速率更慢。

我认为应该以更优雅的方式用不同的方法实现我的目标。请劝告。

4 个答案:

答案 0 :(得分:3)

在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); }

现在你每毫秒都会得到一个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.
}

现在你将每毫秒发送一个数据包,因为系统可以保持这种状态。后者很重要 - 如果系统负载过重(或者您创建数据包的代码太慢),下一个信号将在下一次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);

现在如果错过了一个警报,你的数据包发送将会变慢(你会错过一个插槽),但是你会在stderr上收到一条消息。


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

答案 1 :(得分:2)

如果你希望它保持一致,你应该将你的过程归结为一个核心,并且&#34;燃烧&#34;通过在底部有_mm_pause()的繁忙循环中旋转核心。然后,您将在每次循环迭代中检查时钟,如果已经过了适当的时间,则启动数据包。

如果你想要保持一致,那么你应该从一开始就计算时间,然后在下一次超时时间内&&#34;每次发送数据包时都会变量。但是要小心:当计算机处于睡眠状态时会很容易发生故障,然后回过头来认为是时候发射100000个数据包了(只要问问Adobe,他们十年前在Flash中有这个bug)

哦,显然不要在笔记本电脑或手机等限电设备上这样做。

答案 2 :(得分:2)

您可以实现内核模式组件来发送UDP数据包,这可以避免用户和用户之间的上下文切换。内核模式。同时,您可以访问高精度计时器,几乎实时发送数据包。

答案 3 :(得分:1)

使用2个线程。 主线

pthread_mutex_lock(&sendLock);
/* create worker thread to send packet */
while (counter < 4294967295) {
    pthread_mutex_unlock(&sendLock);
    /* nano sleep for very small interval */
    sched_yield();
    pthread_mutex_lock(&sendLock);

    /* nano sleep for remaining time interval */
}

同时在工作线程中

while (1) {
    pthread_mutex_lock(&sendLock);
    pthread_mutex_unlock(&sendLock);
    /* udp send code and counter increment*/ 
    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++;
    sched_yield();
}

这将确保每个时间间隔发送1个数据包