DatagramSocket port binding problem, and solution


#1

I'm using DatagramSocket to receive UDP packets on a port for a PnP functionality, and I could have several instances listening on the same port. Today this fails, since bindToPort in DatagramSocket constructor fails for subsequent instances for the same port. With UDP, I would've expected any instances to be able to listen in on the same port. Well, anyway, changing constructor to:


DatagramSocket::DatagramSocket (const int localPortNumber, const bool canBroadcast)
    : portNumber (0),
      handle (-1),
      connected (true),
      allowBroadcast (canBroadcast),
      serverAddress (nullptr)
{
    SocketHelpers::initSockets();
    handle = (int) socket (AF_INET, SOCK_DGRAM, 0);
    
    const int reuse = 1;
    setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof (reuse));

    bindToPort (localPortNumber);
}

seems to do the expected, both on Windows and on Linux, and then presumably on Mac/Android aswell, though not tested.

 


#2

Not being much of an expert on the finer details of sockets, I'm happy to go along with your advice on this one.. Anyone else have a view?


#3

The downside to this is that anyone else's application can bind to the same port number. With some protocols this is problematic because the instances would be indistinguishable to other 'nodes' (since they would be the same IP4:<port>).

In practice, it may not be much of a problem, but when I addressed this for my purposes a couple of years ago I added a 'share address' boolean argument to the constructor and made the default value false.

 


#4

That would work for me aswell.


#5

Is that probably only one of your sockets will receive incoming data. So you have to manage who gets the data and what to do with it. In which case, why use more than one socket anyway? Since all your outgoing data has a destination address, there's not much benefit.

 

Bruce


#6

I have an application that reads broadcast data form a UDP port and it work well by opening a DatagramSocket and read from it. But I also need to run multiple instances of the sample application on the same computer. When I try to open multiple instances of the same application (different processes) on the same Mac, it failed on the second instance binding. This probably is due to the fact that the first instance is still holding the port and does not allow other applications to bind to the port. But the same applicaiton running on Windows works fine and I can open up multiple instances on same Windows PC.

After some reading, I found that in juce_Socket.cpp, the makeResuable method set up SO_REUSEADDR for the socket. In order to allow the port to be useable by other process, it also need to set SO_REUSEPORT as well. I tried adding another setsockopt line to set SO_REUSEPORT, then it works okay for me on Mac. 

The reason that Windows works fine is because in Windows setting SO_REUSEADDR will imply SO_REUSEPORT as well. (As in Windows, they do not support SO_REUSEPORT explicitely). 

I know my need to have open multiple process and bind to same port might have security risk. But is it possible to have option on DatagarmSocket class so that application can choose whether they want to share the port for other process.

Eddie


#7

OK i've added support for this on the latest tip. Does this work for you?


#8

In addition to breaking building on Android, the explanation above is not quite correct. The issue wasn't just needing two options - because the options are disjointed with SO_REUSEPORT being recent (in ancient programmer years) and inconsitently implemented. It is when, relative to binding, things are set (EDIT: See reponse to Eddie below). In my response in the Android section I linked to a long (but very good) explanation of the two options (though it is now slightly obsolete, since SO_REUSEPORT has been depricated again on some Linux variants).

 


#9

Eddie:

The issue is probably your overall address usage. I do something very similiar to what you describe all the time without ever needing SO_REUSEPORT.

I suspect that you are in a situation of two instances using the same source address, talking to the same destination address, on the same port.

Adding SO_REUSEPORT may have made it seem to work, but the behavior is generally indeterminate. Linux used to have a weird hack so that each app would get a portion of the datagrams, but there was no control over who got what... Probably why it is now seemingly depricated. In the post I referred to in my response to Fabian, there is a link explaining the options and addressing in some detail.

However, if you want to explain just a little more of what you are trying to do communication/datagram wise, I'd be happy to try to help. I suspect that is could be as simple as making sure that the different instances of your app on Mac aren't using identical indentifiers. If you change just one aspect of the three items I mention above, for example if:

myapp:127.1.1.10 someapp:6.6.6.6 port12 (connection 1)   myapp:127.1.1.10 someapp:6.6.6.6 port12 (connection 2) 

becomes:

MYAPP1:127.1.1.10 someapp:6.6.6.6 port12 (connection 1)   MYAPP2:127.1.1.10 someapp:6.6.6.6 port12 (connection 2)

You should not have to tell sockets to let you do identifcal binding.

Windows doesn't work because of the meaning of the option, Windows works because running multiple instances is routine, so the Sockets implementation uses the instance handle instead of process name for unique address identification (it is actually a bit more complicated, but that is the general idea).
 


#10

Dear Fabian,  Jfitzpat,

Thank you both for your help and suggestion. It is working great now.

1. I got the newest tip, and the support is working okay, except this will not work on Android. But it is okay,  as I will not be running 2 instances of the same app on Android environment anyway.

2. Thanks Jfitzpat's explaination, that make sense. For my application, basically both of my instanaces are NOT talking to each other. They both act as a server to listen to the same UDP port on the same running host. The source of UDP packet is from another network device bumping out UDP broadcast 255.255.255.255 message to the network. And I need both of the instacnces to receive the same broadcast packet.

I have tested this in Mac and Windows PC, they seems to be running okay. I have not tested Linux yet, will give it a try too. For Android and iOS, I will not need to run 2 instances on it. So it is okay too.

Thanks again.

Eddie