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

19

我有一个客户端通过UDP广播发送数据。(假设是到127.0.0.255:12345)

现在我想要有多个服务器监听这些数据。在本地机器上,为了实现这个目的,它们需要共享监听端口12345。

我的问题是,这是否可行,是否存在任何不利因素,并且这种方法可能会出现什么问题。

有一种替代方案,但遗憾的是会带来很多额外开销:
实现某种类型的注册过程。在启动时,每个服务器告诉客户端它的端口。 然后客户端将消息发送到每个端口(需要多次发送数据,必须实现某种握手...)
您知道是否有更好的替代方案吗?

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


我曾尝试使用来自 boost::asio::udp::socket 的以下方法 set_option(udp::socket::reuse_address(true)); 但没有成功... 关于这方面几乎没有文档,有人有提示吗? - MOnsDaR
1
请参见https://dev59.com/rWYq5IYBdhLWcg3wjBQM。 - Eero Aaltonen
3个回答

30

您需要在两个进程中都使用SO_REUSEPORT选项绑定套接字。如果您在第一个进程中没有指定此选项,那么在第二个进程中进行绑定将失败。同样,如果您在第一个进程中指定了此选项,但在第二个进程中没有指定,那么在第二个进程中进行绑定也会失败。该选项有效地同时指定了请求(“我要绑定到这个端口,即使它已经被另一个进程绑定了”)权限(“其他进程也可以绑定到这个端口”)。

有关更多信息,请参见此文档的第4.12节。


我将此答案标记为“正确”,因为它表明无法同时在一个端口上监听多个服务器。链接的网址支持此答案。如果发布了另一个回答,表明这是可能的或者有更详细的解释,我会将新的答案标记为接受的,而不是这个答案。 - MOnsDaR
1
@MOnsDaR: 你说的“不可能”是什么意思?根据那份文档来看:“SO_REUSEPORT 标志允许多个进程绑定到同一个地址,只要它们都使用了 SO_REUSEPORT 选项。” - cdhowie
3
它并没有展示任何这样的东西。它展示了这是可能的,并且展示了如何做到。 - user207421
我可能在使用Boost::Asio时遇到了问题。似乎没有添加REUSEPORT选项,也没有激活的可能性。很抱歉让您感到困惑,根据文档,应该是可以实现的。 - MOnsDaR

7

本回答参考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方法失败的原因。


1
当然,自那时以来Linux已经增加了这个功能(自内核3.9版本)。在这里可以看到有趣的信息:http://lwn.net/Articles/542629/ -- 然而,SO_REUSEPORT意味着多个服务器可以接受数据报,而不是它们都接收相同的数据报。 - Alexis Wilke

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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接