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

Posted

技术标签:

【中文标题】让两个 UDP 服务器监听同一个端口?【英文标题】:Let two UDP-servers listen on the same port? 【发布时间】:2011-05-20 20:23:41 【问题描述】:

我有一个通过 UDP 广播发送数据的客户端。 (比如说 127.0.0.255:12345)

现在我想让多个服务器监听这些数据。要在本地机器上这样做,他们需要共享端口 12345 进行监听。

我的问题是,如果可能的话,是否有任何缺点,以及这种方法是否存在问题。

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

如果这很重要: 我将 C++ 与 Boost::Asio 一起使用。软件应该是可移植的(主要是 Linux 和 Windows)。

【问题讨论】:

我尝试使用 boost::asio::udp::socket set_option(udp::socket::reuse_address(true)); 中的以下方法但没有成功...几乎没有关于此的文档,有人有提示吗关于那个? 另见***.com/questions/14388706 【参考方案1】:

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

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

【讨论】:

我将此答案标记为“正确”,因为它表明无法同时在 1 个端口与多个服务器进行侦听。链接的 url 支持这个答案。如果发布的另一个答案表明它是可能的或有更详细的解释,我会将新答案标记为已接受而不是这个。 @MONsDaR:“不可能”是什么意思?来自该文档:“SO_REUSEPORT 标志允许多个进程绑定到同一个地址,前提是它们都使用 SO_REUSEPORT 选项。” 它没有显示任何类似的东西。它表明它可能的,它表明如何。 我可能遇到了 Boost::Asio 的问题。似乎 REUSEPORT 没有添加在那里,也没有可能激活它。对不起,我的困惑,根据文件,应该是可能的。【参考方案2】:

这个答案引用了 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/2851754http://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 的方法在尝试与便携式软件一起使用时会失败。

【讨论】:

当然,从那时起 Linux 就获得了附加功能(从内核 3.9 开始)。在此处查看有趣的信息:lwn.net/Articles/542629 -- 然而,SO_REUSEPORT 意味着多个服务器可以接受数据报,而不是它们都接收相同的数据报。【参考方案3】:

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

但是如果你在socket上设置reuse_address(true)(或者在使用TCP时在acceptor上),然后绑定socket,它会允许多个应用程序,或者你自己应用程序的多个实例再做一次,每个人将收到所有消息。

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

【讨论】:

以上是关于让两个 UDP 服务器监听同一个端口?的主要内容,如果未能解决你的问题,请参考以下文章

多个UDP监听同一个端口

java 用ServerSocket监听了一个端口,在程序结束的

在特定端口监听 UDP 消息

UDP通讯协议

检测udp端口

如何使用ZeroMQ监听和解析特定端口上的UDP数据?