4

For an UDP server architecture that will have long-lived connections, one architecture is to have one socket that listens to all incoming UDP traffic, and then create separate sockets for each connection using connect() to set the remote address. My question is whether it is possible to do this atomically similar to what accept() does for TCP.

The reason for creating a separate socket and using connect() is that this makes it easy to spread the packet-processing across multiple threads, and also make it easier to have the socket directly associated with the data structures that are needed for processing. The demultiplexing logic in the networking stack will route the incoming packets to the most specific socket.

Now my question is basically what happens when one wants to emulate accept() for UDP like this:

  1. Use select() with a fd-set that includes the UDP server-socket.

  2. Then read a packet from the UDP server-socket.

  3. Then create a new UDP socket which is then connect()ed to the remote address

  4. I call select() with a fd-set that includes both sockets.

  5. What is returned?

given that a packet arrives to the OS somewhere between 1 and 3.

Will the packet be demultiplexed to the UDP server-socket, or will it be demultiplexed to the more specific socket created in 3. That is, at what point does demultiplexing take place? When the packet arrives, or must it happen "as if" it arrived at point 4?

Follow-up question in case the above does not work: What's the best way to do this?

  • Are you sure you can even create a new UDP socket connected to the other end, and still maintain the same server side port ? What you'd usually do is create an UDP socket on a different port on the server side, the "server socket" is used only for the initial request, further communication with a peer takes place on 2 entierly different port. Or just just use 1 socket on the server ,and don't care about connecting it, you just need to note the peers address. UPD is connectionless anyway. – nos Jun 30 '09 at 16:25

3 Answers3

1

I see that this discussion is from 2009, but since it keeps popping up when I search, I thought I should share my approach. Both to get some feedback and because I am curios about how the author of the question solved the problem.

The way I chose emulate UDP-accept was a combination of number one and two in nik's answer. I have a root thread which listens on a given socket. I have chosen to use TCP for simplicity, but changing this socket to UDP is not very hard. When a client wants to "connect" to my server using UDP, it first connects to the TCP socket and requests a new connection.

The root thread then proceeds by creating a UDP socket, binds it to a local interface, does connect and sets up data structures. This file descriptor is then passed to the thread that will be responsible for the connection. The IP/port information of the new UDP socket is passed back to the client, which creates a new UDP socket and sends data to the provided IP/port.

This approach works well for my use, but the additional steps for setting up a flow introduces an overhead. In some cases, this overhead might not be acceptable.

Kristian Evensen
  • 1,315
  • 13
  • 14
1

I found this question after asking it myself here...

UDP server and connected sockets

Since connect() is available for UDP to specify the peer address, I wonder why accept() wasn't made available to effectively complete the connected UDP session from the server side. It could even move the datagram (and any others from the same client) that triggered the accept() over to the new descriptor.

This would enable better server scalability (see the rationale behind SO_REUSEPORT for more background), as well as reliable DTLS authentication.

user1715587
  • 155
  • 1
  • 10
0

This will not work.
You have two simple options.

  1. Create a multi-threaded program that has a 'root' thread listening on the UDP socket and 'dispatching' received packets to the correct thread based on the source. This is because you want to segregate processing by source.

    • Extend your protocol so the the sources accept an incoming connection on some fixed port and then continue with the protocol communication. In this case you would let the source request on the standard UDP port (of your choice), then your end will respond from a new UDP socket to the sources' UDP port. This way you have initiated a new UDP path from your end backwards to the known UDP port of each source. That way you have different UDP sockets at your end.
nik
  • 13,254
  • 3
  • 41
  • 57
  • Why will this not work? See http://lxr.linux.no/linux-bk+v2.6.5/net/ipv4/udp.c#L222 for an example of an OS that will demultiplex to the most specific socket. My issue with your proposed solutions is that solution 1 might not scale, and packets generated by solution 2 might not reach clients behind certain NATs. –  Jun 30 '09 at 20:39
  • Ok, I had no idea you were willing to code under the socket layer. – nik Jul 01 '09 at 05:58