我需要为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检查,则会使数据包速率更慢。
我认为应该以更优雅的方式用不同的方法实现我的目标。请劝告。
答案 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个数据包