19

I have a client which sends data via UDP-broadcast. (To let's say 127.0.0.255:12345)

Now I want to have multiple servers listening to this data. To do so on a local machine, they need to share the port 12345 for listening.

My question is, if that is possible, if there are any disadvantages and if there could be problems with this approach.

There is one alternative which unfortunately brings with a lot of overhead:
Implement some kind of registration-process. On startup, each server tells the client its port. The client then sends the messages to each port (having to send the data multiple times, some kind of handshaking needs to be implemented...)
Do you know any better alternative?

If that matters:
I'm using C++ with Boost::Asio. The software should be portable (mainly Linux and Windows).

MOnsDaR
  • 8,401
  • 8
  • 49
  • 70
  • I've tried to use the following method from boost::asio::udp::socket `set_option(udp::socket::reuse_address(true));` with no success... There is almost no documentation about that, does anyone has a hint about that? – MOnsDaR Dec 06 '10 at 08:32
  • 1
    See also http://stackoverflow.com/questions/14388706/ – Eero Aaltonen Jan 30 '13 at 14:50

3 Answers3

30

You will have to bind the socket in both processes with the SO_REUSEPORT option. If you don't specify this option in the first process, binding in the second will fail. Likewise, if you specify this option in the first but not the second, binding in the second will fail. This option effectively specifies both a request ("I want to bind to this port even if it's already bound by another process") and a permission ("other processes may bind to this port too").

See section 4.12 of this document for more information.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • I marked this answer as "correct" because it shows that it is not possible to listen at 1 port with serveral server at the same time. The linked url supports this answer. If another answer is posted which shows that it is possible or which has a more detailled explanation, i'll mark the new answer as accepted instead of this. – MOnsDaR Dec 06 '10 at 13:02
  • 1
    @MOnsDaR: What do you mean "not possible?" From that document: *"The SO_REUSEPORT flag allows multiple processes to bind to the same address provided all of them use the SO_REUSEPORT option."* – cdhowie Dec 06 '10 at 18:15
  • 3
    It doesn't show anything of the sort. It shows that it *is* possible and it shows *how.* – user207421 Dec 06 '10 at 22:52
  • I probably ran into problems with Boost::Asio. It seems as if REUSEPORT is not added there and there is no possibility to activate it. Sorry for my confusion, according to the document, it should be possible. – MOnsDaR Dec 07 '10 at 14:10
7

This answer is referenced to the answer of cdhowie, who linked a document which states that SO_REUSEPORT would have the effect I'm trying to achieve.

I've researched how and if this option is implemented and focused mainly on Boost::Asio and Linux.

Boost::Asio does only set this option if the OS is equal to BSD or MacOSX. The code for that is contained in the file boost/asio/detail/reactive_socket_service.hpp (Boost Version 1.40, in newer versions, the code has been moved into other files).
I've wondered why Asio does not define this option for platforms like Linux and Windows.

There are several references discussing that this is not implemented in Linux: https://web.archive.org/web/20120315052906/http://kerneltrap.org/mailarchive/linux-netdev/2008/8/7/2851754
http://kerneltrap.org/mailarchive/linux-kernel/2010/6/23/4586155

There also is a patch which should add this functionality to the kernel: https://web-beta.archive.org/web/20110807043058/http://kerneltrap.org/mailarchive/linux-netdev/2010/4/19/6274993

I don't know if this option is existing for Windows, but by defining portable as an attribute for software which runs on Linux too, this means, that SO_REUSEPORT is OS specific and there is no portable solution for my question.

In one of the discussions I've linked it is recommended for UDP to implement a master-listener which then provides the incoming data to multiple slave-listeners.

I will mark this answer as accepted (though feeling kind of bad by accepting my own answer), because it points out why the approach of using SO_REUSEPORT will fail when trying to use it with portable software.

pestophagous
  • 4,069
  • 3
  • 33
  • 42
MOnsDaR
  • 8,401
  • 8
  • 49
  • 70
  • 1
    Of course since then Linux got the added feature (since kernel 3.9). See interesting info here: http://lwn.net/Articles/542629/ -- however, the `SO_REUSEPORT` means that multiple servers can accept datagrams, not that they all receive the same datagram. – Alexis Wilke Oct 15 '14 at 11:29
4

Several sources explain that you should use SO_REUSEADDR on windows. But none mention that it is possible to receive UDP message with and without binding the socket. The code below binds the socket to a local listen_endpoint, that is essential, because without that you can and will still receive your UDP messages, but by default your will have exclusive ownership of the port.

However if you set reuse_address(true) on the socket (or on the acceptor when using TCP), and bind the socket afterwards, it will enable multiple applications, or multiple instances of your own application to do it again, and everyone will receive all messages.

// Create the socket so that multiple may be bound to the same address.
boost::asio::ip::udp::endpoint listen_endpoint(
    listen_address, multicast_port);

// == important part ==
socket_.open(listen_endpoint.protocol());
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(listen_endpoint);
// == important part ==

boost::array<char, 2000> recvBuffer;
socket_.async_receive_from(boost::asio::buffer(recvBuffer), m_remote_endpoint,
        boost::bind(&SocketReader::ReceiveUDPMessage, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)
Jan Wilmans
  • 665
  • 6
  • 10