让两个UDP服务器监听同一个端口?

时间:2010-12-06 08:21:14

标签: networking network-programming udp

我有一个通过UDP-broadcast发送数据的客户端。 (比方说127.0.0.255:12345)

现在我想让多个服务器监听这些数据。要在本地计算机上执行此操作,他们需要共享端口12345以进行收听。

我的问题是,如果可能,如果有任何不利之处,并且这种方法可能存在问题。

有一种替代方案不幸会带来很多开销:
实施某种注册流程。在启动时,每个服务器都告诉客户端它的端口。然后客户端将消息发送到每个端口(必须多次发送数据,需要实现某种握手......)
你知道更好的选择吗?

如果重要:
我正在使用C ++和Boost :: Asio。该软件应该是可移植的(主要是Linux和Windows)。

3 个答案:

答案 0 :(得分:23)

您必须使用SO_REUSEPORT选项将两个进程中的套接字绑定。如果未在第一个进程中指定此选项,则第二个进程中的绑定将失败。同样,如果在第一个而不是第二个中指定此选项,则第二个中的绑定将失败。此选项有效地指定了一个请求(“我想绑定到此端口,即使它已经被另一个进程绑定”)一个权限(“其他进程也可以绑定到此端口”)。 / p>

有关详细信息,请参阅this document的第4.12节。

答案 1 :(得分:5)

这个答案引用了cdhowie的答案,cdhowie链接了一个文件,该文件指出SO_REUSEPORT会产生我想要达到的效果。

我研究了如何以及是否实现此选项,主要关注Boost :: Asio和Linux。

Boost :: Asio仅在操作系统等于BSD或MacOSX时才设置此选项。其代码包含在文件boost/asio/detail/reactive_socket_service.hpp中(Boost版本1.40,在较新版本中,代码已移至其他文件中)。
我想知道为什么Asio没有为Linux和Windows等平台定义这个选项。

有几篇参考文献讨论这在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

还有一个补丁应该将此功能添加到内核: https://web-beta.archive.org/web/20110807043058/http://kerneltrap.org/mailarchive/linux-netdev/2010/4/19/6274993

我不知道这个选项是否适用于Windows,但是通过将portable定义为在Linux上运行的软件的属性,这意味着,SO_REUSEPORT是特定于操作系统的,并且没有可移植的解决方案我的问题。

在我讨论的其中一个讨论中,建议UDP实现一个主监听器,然后将监听器提供给多个从监听器。

我会将此答案标记为已接受(虽然接受我自己的答案感觉有点不好),因为它指出了在尝试将其与便携式软件一起使用时,使用SO_REUSEPORT的方法会失败的原因。

答案 2 :(得分:4)

有几个消息来源解释说你应该在Windows上使用SO_REUSEADDR。但没有人提到可以使用和接收UDP消息而不用绑定套接字。 下面的代码将套接字绑定到本地listen_endpoint,这是必不可少的,因为没有它,您仍然可以接收UDP消息,但默认情况下您将拥有该端口的独占所有权。

但是,如果在套接字上(或使用TCP时在接受器上)设置了reuse_address(true),并在之后绑定套接字,它将启用多个应用程序,或者您自己的应用程序的多个实例再次执行它,并且每个人将收到所有消息。

// 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)