Neil_UK
Neil_UK

Reputation: 1083

UDP buffers, where are they, what's guaranteed?

I'm experimenting with the python socket library (3.5, on linux mint 18), trying to understand UDP. I'm a hardware person dabbling in software, and UDP seems simpler to get my head around than TCP. I am well aware that UDP does not guarantee to deliver packets one for one.

So far, I can follow the tutorials to echo data back from a server to a client.

However, I like to push things to see what happens when applications don't follow the expected path, I detest writing things that 'hang' when unexpected things happen.

If a server binds a socket to a port number, then the client sends several messages to that port, before the server calls recvfrom() several times, I find that each call returns one message, with the messages in order. In other words, the messages have been buffered, later messages have not overwritten earlier messages in the queue. I was not surprised to see this happen, but also would not have been surprised to find only the last received message available, aka buffer length of one.

Is this buffer, and its depth, a python implementation detail, a linux mint/ubuntu detail, or defined by the UDP protocol?

Upvotes: 4

Views: 13814

Answers (3)

Mecki
Mecki

Reputation: 133189

Network I/O is performed by the operating system kernel, your code runs in userspace, so it's just natural that when network data arrives, the kernel has to store that data somewhere in kernel memory until your userspace code is able to fetch it.

Keep in mind that even when you read as fast as possible from a socket, whenever you've read a packet and process it, two other packets could arrive while you are still busy processing and you wouldn't want those packets to be lost, would you? Sure, UDP is not a reliable protocol, so packets may never arrive at all but if they did make it to your computer, how stupid would it to drop them there, just because nobody is currently actively waiting for the packets? And writing networking code must also be possible without being forced to write mutlithreaded code. Even a single threaded program must be able to do networking, yet such a program can never listen for new packets and process previous packets at the very same time.

Protocol standards typically don't define how protocol implementation work in detail, they only define what packets are supposed to look on the wire and if the protocol defines a behavior, they define the behavior as seen from the perspective of an outside entity (like "If that goes into your system, the following is expected to come out of it in return"). However how an implementation makes that happen is up to the implementation. Even if the standard would define how an implementation must do it, how would anyone notice you are violating the standard? As long as the behavior looks correct from outside the system, nobody would ever notice that the system does not work internally as the standard demands. And also why would anyone care? As long as the behavior is exactly as if you were following the standard, what difference does it make if you truly follow it internally or not?

UDP does not require any buffering but without it, a single threaded program could barely ever use UDP as it would constantly use huge amounts of packets. Imagine a hose with water dripping out of it all the time. Your task is to catch this water with a cup and tip it into a pipe. Whenever your cup is full and you run to the pipe, all the water from the hose is lost because you are not there to catch it. Therefore, it is better to put the hose in a bucket so that the bucket catches the water and you get the water from the bucket with the cup. As long as you siphon the water out of the bucket faster than it drips out of the hose, the bucket will never overflow and you will never lose water, even if you are not constantly hanging directly on the hose.

Sockets always have a buffer in modern operation systems, the only difference is that in case of UDP this buffer is an internal implementation detail, whereas in case of TCP the protocol requires the existence of buffers on both ends, otherwise TCP cannot work as intended. However, if the TCP buffer equals the socket buffer or if there is an independent buffer in front of the socket buffer or if there is such a buffer in the protocol implementation and therefor no socket buffer at all, this again is an implementation detail the protocol standard doesn't care for (usually the socket buffer is simply used as TCP buffer, as having two buffers has no practical advantage and the socket buffer is there anyway).

The operating system will choose a default buffer size for every sockets, maybe depending on the protocol of the socket, maybe not. Some systems will dynamically adjust the buffer size while you are using the socket (e.g. depending on throughput seen, sometimes also on latency when known) and most systems allow you to adjust the buffer sizes yourself if desired.

Upvotes: 1

Barmar
Barmar

Reputation: 782166

UDP buffers are in the operating system's network stack. The size of the buffers will depend on how much memory your computer has and kernel configuration settings. On modern computers with gigabytes of memory, it's likely that the OS will have plenty of space for UDP buffers, and it will be difficult to overflow them unless the computer is extremely overloaded.

There might be some way for you to configure the OS to limit the amount of memory used for UDP buffers, so that you can cause overflows and see what the symptoms are in your test application. I don't know the configuration settings, you could try asking in Unix & Linux or AskUbuntu.com.

Upvotes: 1

Jeremy Friesner
Jeremy Friesner

Reputation: 73279

Is this buffer, and its depth, a python implementation detail, a linux mint/ubuntu detail, or defined by the UDP protocol?

The UDP socket's buffer sizes are an implementation detail of your OS's networking stack. Each OS tries to set reasonable default size based on its expected use-cases, but you can override the OS's default size (up to some maximum value, anyway) on a per-socket basis by calling socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, newSizeInBytes) and/or socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, newSizeInBytes)

The buffers will queue up as many packets as they have space to hold, then drop any incoming packets that they can't fully fit into the remaining space.

Upvotes: 4

Related Questions