Python Twisted框架在特定接口上绑定多播
我在谷歌上到处搜索,还是没找到关于以下问题的明确答案:
根据以下指南大致了解后:
http://twistedmatrix.com/documents/10.2.0/core/howto/udp.html#auto3
我想知道如何将Twisted的多播监听器绑定到仅仅多播地址,并且可以选择特定的网络接口或所有接口。
在查看reactor.listenMulticast时,我发现它并没有提供针对硬件接口的抽象,只是用一个IP地址表示的伪接口。我找不到一个方法来仅绑定到特定接口或所有接口上的多播地址,比如224.0.0.1。
有没有人能提供更多的信息呢?
3 个回答
一种简单但有点笨重的方法就是直接通过类似下面的方式来指定一个路径:
sudo route add 239.255.0.0 -interface en0 -netmask 255.255.0.0
其他的回答可能能解决问题,但有一种“更复杂”(而且可能更简单)的方法,可以在多个接口上监听一个多播组。
在一个或多个接口上监听多播组
要在一个或多个接口上监听多播组,你需要在多次调用协议的transport.joinGroup()方法时,把每个想要的接口的IP地址作为'interface'参数提供。
下面是一个在我的Linux系统上能工作的例子,它会让你的系统在特定的接口上监听多播。记得把IP地址换成你自己系统上的地址。
#!/usr/bin/env python
from twisted.internet import protocol
from twisted.internet import reactor
class MyProtocol(protocol.DatagramProtocol):
def startProtocol(self):
# Join a multicast group, 224.0.0.9, on the interface belonging
# to each of these IPs.
# XXX Replace the interface_ips with one or more IP addresses belonging
# to your system.
for interface_ip in ["192.168.2.2", "10.99.1.100"]:
self.transport.joinGroup("224.0.0.9", interface_ip)
if __name__ == "__main__":
reactor.listenMulticast(1520, MyProtocol())
reactor.run()
你可以使用 /sbin/ip maddr show
命令来检查接口是否在监听新的多播组。在命令的输出中找到你想要的接口名称,确认多播组是否显示在它下面。
原帖中链接的UDP服务器例子也可以通过修改joinGroup()的调用,加入第二个IP地址参数来实现同样的功能。
从特定IP发送多播
如果你在一个套接字上接收多播数据,你可能也想发送多播数据——可能是通过多个接口。因为这两个功能关系密切,而且相关的例子很少,所以我在这里也提一下。在一个twisted.internet.protocol.DatagramProtocol对象中,你可以使用self.transport.setOutgoingInterface()方法来控制后续调用self.transport.write()时使用的源IP。下面是一个从多个IP/接口发送消息的例子:
class MyProtocol(protocol.DatagramProtocol):
# ...
def send_stuff(self, msg):
for src_ip in ["10.0.0.1", "192.168.1.1"]:
self.transport.setOutgoingInterface(src_ip)
self.transport.write(msg, ("224.0.0.9", 1520))
假设这些IP被分配给两个不同的接口。使用Wireshark抓包时,你会看到消息是从第一个接口发送出去的,然后是第二个接口,每个IP作为各自传输的源IP地址。
reactor.listenMulticast
会返回一个 twisted.internet.udp.MulticastPort
对象。这个对象管理着你正在监听的套接字。所以要记住保存 reactor.listenMulticast
的结果,并设置合适的套接字选项(在这个例子中是 SO.BINDTODEVICE),同时还需要一个以空字符结束的设备字符串。
import IN, socket, struct
udp_port = reactor.listenMulticast(8005, MulticastServerUDP())
dev = "eth0" + "\0"
udp_port.socket.setsockopt(socket.SOL_SOCKET, IN.SO_BINDTODEVICE, dev)
reactor.run()
如果这个功能能直接通过 listenMulticast
调用来实现,那就更好了。不过假设这样做有效的话,修复起来应该也不难。如果这个方法能解决你的问题,请告诉我。