Twisted:出站连接的源IP地址
我正在开发一个服务,这个服务是用Python和Twisted框架写的,运行在Debian GNU/Linux上,主要功能是检查SIP服务器的可用性。为此,我使用了OPTIONS方法(这是SIP协议的一部分),因为这似乎是一个常见的做法。为了构建正确且符合RFC标准的请求头,我需要知道即将建立连接的源IP地址和源端口。请问在Twisted中怎么做到这一点呢?
这是我尝试过的做法:我创建了一个新的类,继承自protocol.DatagramProtocol,然后在startProtocol(self)
方法中,我使用了self.transport.getHost().host
和self.transport.getHost().port
。后者确实是将要使用的端口,而前者却只返回了0.0.0.0。
我猜此时Twisted还不知道将使用哪个网络接口,因此也不知道源IP地址是什么。Twisted有没有提供什么方法可以帮助我解决这个问题,还是我需要用其他方式与操作系统(路由)进行交互?或者我是不是错误地使用了self.transport.getHost().host
?
3 个回答
你有没有注意到你想做的事情是否可以通过Twisted中的SIP实现来完成?
无论如何,在Twisted中设置UDP的源地址和端口的方法和不使用Twisted时是很相似的。在Twisted中,reactor.listenUDP(port, protocol, interface)
这个命令会把一个UDP的套接字绑定到特定的端口和接口上,并把接收到的数据包交给你的协议来处理。在这个协议里,self.transport.write(msg, addr)
这个命令会把一个数据包发送到addr
,使用的是协议绑定的地址作为源地址。
再读一遍你的问题,我觉得你唯一缺少的就是把interface
传给reactor.listenUDP(...)
。
如果你在使用UDP协议,那么端点的确定方式有两种:
- 在套接字上调用
bind()
方法,并明确给它一个地址 - 发送一个数据包
如果你想了解更多细节,可以查看这个回答。
我对twisted这个库不是很熟悉。根据我快速浏览源代码的情况,似乎你可能需要使用一个叫做 t.i.d.SelectReactor
的反应器。这就是t.n.d.DNSDatagramProtocol
在后台做的事情。
如果把 twisted
去掉,下面的代码片段展示了发生了什么:
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
<socket._socketobject object at 0x10025d670>
>>> s.getsockname() # this is an unbound or unnamed socket
('0.0.0.0', 0)
>>> s.bind( ('0.0.0.0', 0) ) # 0.0.0.0 is INADDR_ANY, 0 means pick a port
>>> s.getsockname() # IP is still zero, but it has picked a port
('0.0.0.0', 56814)
如果你需要支持多个网络接口或者IPv4和IPv6,获取主机名就会复杂一些。如果你能让使用的接口可配置,那么就把它作为元组的第一个元素传给 socket.bind()
,这样就可以了。
现在困难的部分是如何在twisted提供的抽象框架内做到这一点。不幸的是,我在这方面帮不了太多。我建议你寻找一些示例,看看如何访问底层的套接字,或者找到一种方法将套接字信息传递给框架。
祝你好运。
为了完整起见,我来回答我自己的问题:
在尝试确定主机的源IP地址之前,确保你在传输层使用了connect()。下面的代码片段展示了协议实现中相关的部分:
class FooBarProtocol(protocol.DatagramProtocol):
def startProtocol(self):
self.transport.getHost().host # => 0.0.0.0
self.transport.connect(self.dstHost, self.dstPort)
self.transport.getHost().host # => 192.168.1.102