Wandering Thoughts archives

2013-06-29

connect() plus write() versus sendto() for UDP sockets

The README that I wrote for my Go version of netcat contains a little aside about the 'listen on a UDP socket' mode of each program:

This is how 'nc -lu' behaves for one connection, but the mechanics are different. nc connect()s its UDP socket and then write()s to it; call leaves its UDP socket unconnected and uses sendto(). [...]

(It turns out that what netcat does is version dependent.)

So, does this make any difference? It turns out that the answer is yes, but not in a way that I expected and not really for writing.

First let's talk about write() versus send(), because sendto() is just send() with a destination address. The functional difference between the two is that send() will allow you to set various message flags. In practice there is exactly one such flag that is both useful on UDP sockets and portable: MSG_DONTROUTE, which means that your UDP packet should not be routed.

(If you're writing Linux specific code there are a number of additional flags that may be useful.)

What netcat does when it listens to a UDP socket is actually a little bit weird, so I want to describe it. Having set up the socket with bind() it does a recvfrom(..., MSG_PEEK), takes the address of the other end from the result, connect()s the UDP socket to it, and then winds up immediately calling recvfrom() again (as part of a general activity loop). When it reads things from standard input it will write() them to the (now-connected) UDP socket.

Connecting a bound UDP socket this way has two effects. The first is that you can use write() or a plain send() on the socket. The second is that a connected UDP socket stops accepting packets from other UDP sources, so netcat has converted its listening server socket into a single-connection socket. This is fine for netcat's use but would be bad if you wanted to reuse the socket as a server socket again, since there is no way to un-connect a UDP socket again.

(I suspect that this is done partly so that netcat can use the same core routine to copy things back and forth regardless of whether it's a client or a server and regardless of whether it's using TCP or UDP.)

An un-connected UDP socket used with full recvfrom() and sendto() multiplexes all client communication through the same socket. This is what you need to handle multiple clients (especially concurrently), but as far as I can see it has the drawback that you don't get any errors if the other end quietly goes away (even if the kernel could tell you, for example if the other end is local). Connected UDP sockets will report an error (eventually) under similar circumstances. And of course you need a completely different set of code than for TCP sockets or connected UDP sockets and you'll need to explicitly consider how you'll deal with multiple clients that are all active at once (since, unlike with TCP, you can't ignore all but one socket and let the kernel sort it out for you).

(This is the kind of thing I write up because it makes me do the research, so I can get this down in my own head. For someone who has a more than casual involvement with networks and networking code, I'm sometimes shockingly ignorant on the fine details of the Unix socket API. Probably I should get and read a copy of the classic Stevens work, although there may be a more modern book on this as well; Stevens does date from 1999, and some things have changed significantly since then.)

unix/UDPConnectVsSendto written at 02:33:47; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.