Helder AC
Helder AC

Reputation: 376

Delay increases sometimes when sending buffer via UDP socket

I've developed an MPEG-ts Streamer. It reads the packets from a file and sends them at the right pace to the receiver.

Now everything works fine excluding that I have some lags quite often. I have searched for every possible error in my code. I have optimized my program quite a bit performance wise.

Now I keep a log with the time it takes the sendto() function to send the packet and I also log the difference between when the packet should have been sent and when it is sent.

I noticed that every time the packets are a lot later than average, the time it took the sendto() to send the previous packet is much higher than normal too.

This indicates me that it is the sendto() that is causing those lags every time it somehow takes longer to send a packet. I am using UDP sockets.

Am I maybe doing something wrong with the sockets? Could it be possible that the socket buffer is full and that it actually takes longer to send the packet? Or am I missing something? Is there maybe a way to accelerate the socket or to make it not fill it's buffer completely before sending?

As this is meant to stream video I am quite depending on the performance, mostly for HD where the lags happen more often as the amount of packets is much higher.

Upvotes: 5

Views: 13732

Answers (4)

Seth Noble
Seth Noble

Reputation: 3303

There are only two reasons for sendto() to block:

  • Your outgoing UDP buffer is full, and it needs to wait until space frees up
  • The CPU scheduler simply decides to do something else for a while, as it can with any syscall.

Check the size of your outgoing buffer:

int buff_size;
int len = sizeof(buff_size);

err = getsockopt(s,SOL_SOCKET,SO_SNDBUF,(char*)&size,&len);

Some linux systems default to absurdly small send buffers (only a few kilobytes), so you may need to set it to something larger.

If buffer is larger and sendto() is briefly blocking anyway (how long exactly?) then that's probably just the cost of doing business. Even on a LAN, and certainly on a WAN, latency is going to vary a lot.

Update

To increase the system limit on UDP buffer sizes:

sysctl -w net.core.wmem_max=1048576
sysctl -w net.core.rmem_max=1048576

You can make this permanent by adding the following lines to /etc/sysctl.conf:

net.core.wmem_max=1048576
net.core.rmem_max=1048576

To take advantage of this within your application, you need to use setsockopt():

int len, trysize, gotsize;
len = sizeof(int);
trysize = 1048576+32768;
do {
   trysize -= 32768;
   setsockopt(s,SOL_SOCKET,SO_SNDBUF,(char*)&trysize,len);
   err = getsockopt(s,SOL_SOCKET,SO_SNDBUF,(char*)&gotsize,&len);
   if (err < 0) { perror("getsockopt"); break; }
} while (gotsize < trysize);
printf("Size set to %d\n",gotsize);

Repeat the same thing for SO_RCVBUF. The loop is important because many systems silently enforce a maximum which is less than the max you set with sysctl and when setsockopt() fails, it leaves the previous value untouched. So you have to try many different values until you get one that sticks. Its also important to not test for (gotsize == trysize) because on some systems the result which is set is not actually the same as the one you requested.

Upvotes: 8

m0skit0
m0skit0

Reputation: 25874

Impossible to say without the code, but here are a few hints:

  • It's possible that your code works just fine, and those delays are caused by OS scheduler.
  • Usually, optimizing your code too early could actually lead to performance drops. Optimization is last step.

You can try to "nicer" your process. If this improves performance, it's an OS scheduler issue.

Upvotes: 1

Aaron Digulla
Aaron Digulla

Reputation: 328770

[EDIT] Replace the receiver with something that has a buffer. Or maybe try to send more data than the receiver really needs - it really should have a buffer already. I doubt that there is anyone in the world who builds set-top boxes and who doesn't know that there are network lags and who isn't bankrupt already.

From your explanation, I doubt that the cause is in your code. There are many reasons why it can be slower:

  • The OS might decide to give another process control since you're doing I/O.
  • The network buffer might be full
  • The network might be congested
  • The virus scanner might be interested in the same data that you're reading and steal too many cycles.
  • If you're on Windows: The Aero desktop renders something (the IRQ of the graphics card usually has a higher priority than that of the network card).

Conclusion: Your problem is that the receiver has no buffer. There are really very many reasons that can cause a delay in the sending of the UDP packets and all of them are not under the control of your code.


Original Answer

UDP is not a reliable protocol. There is no guarantee that packets arrive at the receiver in time. UDP just has a smaller overhead than TCP but that comes at the cost of reliability.

The usual solution is to send more data than the client needs. So for example, you send the first 10 seconds at full throttle. This allows the client to cache some data to play over lags.

You should also change the client to send back the length of the cached stream to the server every few seconds. The server can then calculate how many packets to send at full throttle to keep the cache on the client side filled.

Upvotes: 0

mac
mac

Reputation: 5647

Unlike TCP, UDP does not buffer data. Source code would definitively help to understand things better. How big are the packets you are sending? In UDP it would be best to keep the payload at the size of the MTU (1500 bytes)

Upvotes: 1

Related Questions