在Python和Qt之间共享多播接收端口

2 投票
1 回答
883 浏览
提问于 2025-04-18 18:02

我正在创建一个Python 2.7的多播监听器,代码如下:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', PORT))
groupAddress = 0
for byte in [224, 0, 0, 243]:
    groupAddress = (groupAddress << 8) | byte
packedGroupAddress = struct.pack('LL', socket.htonl(groupAddress), socket.htonl(socket.INADDR_ANY))
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, packedGroupAddress)

在另一个用C++和Qt 4.8写的应用程序中,我这样创建了一个多播监听器:

QUdpSocket socket;
if (not socket.bind(PORT, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)) {
    qDebug() << "Binding failed:" << socket.error();
}
socket.joinMulticastGroup(QHostAddress("224.0.0.243"));

这两个都能正常工作,我可以接收到我想要的多播数据包。但是,问题是我不能同时运行这两个程序。如果我先启动Python应用程序,然后Qt就会报错:

Binding failed: QAbstractSocket::AddressInUseError

如果我先运行Qt版本,那么Python就会抛出一个异常,错误信息如下(简化版):

Traceback (most recent call last):
  [...]
  File "/usr/local/lib/python2.7/dist-packages/gdcp/announcer.py", line 196, in _setupSocket
   s.bind(('', PORT))
  File "/usr/lib/python2.7/socket.py", line 224, in meth
   return getattr(self._sock,name)(*args)
socket.error: [Errno 98] Address already in use

我可以多次并行运行这两个版本,而没有出现任何错误,所以地址共享似乎是可以的。问题就出在当我同时使用Python和Qt的套接字时,才会出现这种情况。我是在Ubuntu 14.04上运行的,虽然上面的代码片段也应该能在Windows上运行。我还没能测试Windows是否也会出现同样的问题。

所以,有人能给出解决方案或者至少解释一下为什么会这样吗?

1 个回答

0

我在使用Qt 4.8.5和Java 8的Red Hat Enterprise Linux 6.5上,遇到了一个类似的问题,就是共享多播接收端口。我可以同时运行多个Qt接收客户端或者多个Java接收客户端。但是,如果我运行一种类型的客户端,另一种类型的客户端就无法绑定套接字。

我找到了一部分解决办法,就是在Qt应用程序中,加入多播组后,明确调用setsockopt()来重新设置SO_REUSEADDR这个标志。这样我就可以在启动Qt客户端后再启动Java客户端。所有客户端都能正常接收数据,但如果我在启动第一个Java客户端后再启动更多的Qt客户端,就会绑定失败。如果我关闭所有Java客户端,就可以再打开更多的Qt客户端。但是一旦第一个Java客户端绑定后,就再也无法绑定更多的Qt客户端了。这真是很奇怪。

我的Qt代码现在大致是这样的:

if (_socket->bind(port, QUdpSocket::ShareAddress|QUdpSocket::ReuseAddressHint))
{
  if (_socket->joinMulticastGroup(bindAddr))
  {
    int reuse = 1;
    if (setsockopt(_socket->socketDescriptor(), SOL_SOCKET, SO_REUSEADDR,
                  (char *)&reuse, sizeof(reuse)) < 0)
      qDebug() << "setsockopt() failed";
  }
  else
    qDebug() << "joinMulticastGroup() failed";
}
else
  qDebug() << "bind() failed";

我没有Java客户端的源代码,但我听说它是按照推荐的方式使用java.net.MulticastSocket类的。

撰写回答