How to send UDP packet every 1 ms?

I need to write a Linux application that will periodically send a UDP packet. Ideally, the frequency should be every 1 ms, and the interval between packets should be consistent.

I tried to do this through regular sockets as follows:

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 } 

In the application above, I just populate the UDP packet with a counter value, so I can distinguish them at the end of the receiver.

Basically, this code does the job, but it has the following problems: 1. The frequency is NOT high enough and depends heavily on the performance of the host and other applications. 2. The interval between packets is not agreed, because RTC is used as a reference. However, if I tried to establish an RTC check, this made the packet rate even slower.

I suggest that ther should be a more elegant way to achieve my goals with a different approach. Please advise.

+5
source share
4 answers

The standard way to get a regular beat in linux (or any UNIX for that matter) is to use 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); } 

Now you get a SIGALRM signal every milliseconds, so you start the loop with 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. } 

Now you will send a packet every milliseconds, LONG LAST, AS THE SYSTEM MAY BE COMPLETED. The latter is important - if the system is too heavily loaded (or your code to create the package is too slow), the next signal will come before the next sigwait call and kill your process. If you do not want this, set the signal for SIGALARM signals:

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

Now, if an alarm is skipped, your packets will be slowed down (you skip the slot), but you will get a message about this on stderr.


One major problem with the above is that it depends on the resolution of your system timer. On Linux, this is highly dependent on the CONFIG_HIGH_RES_TIMERS kernel configuration. If this is not enabled, you will probably only have a resolution of 10 ms, so trying to use a 1 ms clock will not succeed. I believe that it is enabled by default for most x86_64 distributions, but you can check by looking at / proc / timer _list and see what the resolution of the "ktime_get_real" clock is.

+2
source

If you want it to be consistent, you must bind your process to one core and โ€œburnโ€ that core, rotating in a closed loop with _mm_pause() at the bottom. Then you will check the clock in each iteration of the loop, and if the correct intervals have passed, trigger the packet.

If you want to be consistent enough, you have to count the time from the very beginning and click the โ€œnext timeoutโ€ variable every time you send a packet. But be careful: it would be easy to start amok when the computer goes to sleep or so, then wakes up and thinks it's time to start 100,000 packages (just ask Adobe, they had this error in Flash about ten years ago).

Oh, and obviously don't do this on a power-limited device like a laptop or phone.

+2
source

You can implement a kernel-mode component to send a UDP packet, which can avoid context switching between user-mode and kernel-mode. At the same time, you can access a high-precision timer to send packets in almost real time.

+2
source

use 2 threads. main topic

 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 */ } 

meanwhile in the workflow

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

it will be a little certain that 1 packet is sent for a period of time

+1
source

Source: https://habr.com/ru/post/1200415/


All Articles