Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send UDP packet every 1 ms?

Tags:

c

linux

sockets

udp

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

I have tried to do it via usual sockets this way:

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 fill the UDP packet with a counter value, so I can distinguish them on the reciever's end.

Basically this code does it's job, but has these problems: 1. Frequency is NOT high enough and highly affected by host perfomance and other applications. 2. Packet interval is not consistent, because there is RTC used as a reference. However if I tried to put RTC check, that made packet rate even slower.

I assume ther should be more elegant way to acheive my goals with different approach. Please kindly advise.

like image 906
Xia Avatar asked Aug 15 '14 13:08

Xia


3 Answers

The standard way of getting a repeating regular heartbeat sort of thing on 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'll be getting a SIGALRM signal every millisecond, so you run your loop with a sigwait call:

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'll be sending a packet every millisecond AS LONG AS THE SYSTEM CAN KEEP UP. The latter is important -- if the system is too heavily loaded (or your code to create a packet is too slow), the next signal will come in before the next sigwait call and kill your process. If you don't want that, put in a signal handler 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 missed, your packet sends will be slowed (you'll miss a slot), but you'll get a message about it on stderr.


One significant issue with the above is that it's dependent on your system timer resolution. On Linux, this depends heavily on the kernel configuration CONFIG_HIGH_RES_TIMERS. If that is not enabled, you likely only have 10ms resolution, so trying to use a 1ms clock will fail badly. I believe it is enabled by default on most x86_64 distributions, but you can check by looking in /proc/timer_list and see what the resolution of the 'ktime_get_real' clock is.

like image 104
Chris Dodd Avatar answered Sep 22 '22 02:09

Chris Dodd


If you want it to be consistent you should affine your process to one core, and "burn" that core by spinning in a busy loop with _mm_pause() at the bottom. You'll then check the clock in each loop iteration, and if the right amount of time has passed, fire off a packet.

If you want to be quite consistent you should count time from the beginning and bump your "next timeout" variable each time you send a packet. But be careful: it would be easy to run amok when the computer is put to sleep or so, then wakes back up and thinks it is time to fire 100000 packets (just ask Adobe, they had this bug in Flash about a decade ago).

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

like image 22
John Zwinck Avatar answered Sep 20 '22 02:09

John Zwinck


You can implement a kernel mode component to send the UDP packet which can avoid the context switches between user & kernel mode. At the same time you can get access to high precision timer to send the packets almost real time.

like image 41
dvasanth Avatar answered Sep 18 '22 02:09

dvasanth