这期内容当中小编将会给大家带来有关python中怎么利用twisted实现TCP通讯,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

创新互联公司服务项目包括定海网站建设、定海网站制作、定海网页制作以及定海网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,定海网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到定海省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
0.写在前面
不管是服务器端还是客户端,都是通过twisted的reactor来启动的,所以首先就需要导入twisted.internet包下的reactor模块
从reactor模块的源码中可以看出reactor模块其实是由多个接口组成的,并且提示了具体内容需要查看twisted.internet包下的interfaces模块中每个接口的具体注释说明
当然从reactor模块的注释中也说明了twisted不止可以用于TCP服务,而是提供了网络方面的API、线程、调度等功能, 但是本次的实验仅仅测试一下TCP的服务器端和客户端
reactor注释中提到的支持的接口:
@see: L{IReactorCore}
@see: L{IReactorTime}
@see: L{IReactorProcess}
@see: L{IReactorTCP}
@see: L{IReactorSSL}
@see: L{IReactorUDP}
@see: L{IReactorMulticast}
@see: L{IReactorUNIX}
@see: L{IReactorUNIXDatagram}
@see: L{IReactorFDSet}
@see: L{IReactorThreads}
@see: L{IReactorPluggableResolver} 根据上面reactor模块的注释,发现和TCP相关的需要查看interfaces模块下的IReactorTCP接口,所以接下来我们移步至IReactorTCP接口
IReactorTCP接口中有两个方法listenTCP和connectTCP,不管从方法名还是其说明都可以看出,前者是监听一个端口提供TCP服务,后者是连接到服务端的TCP客户端
def listenTCP(port, factory, backlog=50, interface=''): def connectTCP(host, port, factory, timeout=30, bindAddress=None):
所以要开启一个TCP服务端,我们需要用到的是listenTCP方法,这个方法有4个参数
| 参数名 | 意义 | 默认值 |
|---|---|---|
| port | 监听的端口,也就是TCP服务启动的端口 | - |
| factory | 服务端的工厂类(根据注释中的提示:详情见twisted.internet包下的protocol模块中的ServerFactory类) | - |
| backlog | 监听队列,响应线程数 | 50 |
| interface | 要绑定到的本地IPv4或IPv6地址,默认为空标示所有IPv4的地址 | ‘’ |
在设置了前两个参数以后,然后通过下面两行代码就可以启动TCP服务了
reactor.listenTCP(port, ServerFactory()) reactor.run()
但是,此时启动的服务是有问题的,当有客户端连接到该服务的时候就会报错
------ File "D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\lib\site-packages\twisted\internet\tcp.py", line 1427, in doRead self._buildAddr(addr)) File "D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\lib\site-packages\twisted\internet\protocol.py", line 140, in buildProtocol p = self.protocol() builtins.TypeError: 'NoneType' object is not callable
根据错误提示,我们找到了问题的原因:
在客户端连接到服务器端的时候,会调用Factory类(ServerFactory的父类)中的buildProtocol方法来建立通讯协议(这里可以理解为客户端和服务器端之间读写的实现方法),其中需要调用self.protocol所指向的方法来初始化这个协议
然而此时的protocol却是None,所以在协议的初始化阶段出错了
再次研究一下protocol模块中的Factory类,发现类方法forProtocol是用于创建factory实例的,但是需要给定一个protocol实例
很好,我们的目标又近了一步,下面继续研究Protocol类
同样在protocol模块中,我们找到了Protocol类,他继承自BaseProtocol类,总共有下面几个方法
BaseProtocol.makeConnection:用于开启连接,当连接开启后会回调connectionMade方法
BaseProtocol.connectionMade:未实现。当连接成功以后回调该方法
Protocol.logPrefix:返回当前类的类名,用于日志log
Protocol.dataReceived:未实现。当收到请求时被调用的方法
Protocol.connectionLost:未实现。当连接断开时调用的方法
所以现在我们只要继承Protocol类,写一个自己的实现协议就可以了,并且只需要实现父类中未实现的3个方法
为了简单一些,在connectionMade和connectionLost方法中我们只记录一下客户端的连接信息并输出一下log,而在dataReceived方法中我们将收到的信息打印出来,并在5s过后返回客户端一条消息
class TcpServer(Protocol)::
CLIENT_MAP = {} # 用于保存客户端的连接信息
def connectionMade(self):
addr = self.transport.client # 获取客户端的连接信息
print("connected", self.transport.socket)
TcpServer.CLIENT_MAP[addr] = self
def connectionLost(self, reason):
addr = self.transport.client # 获取客户端的连接信息
if addr in TcpServer.CLIENT_MAP:
print(addr, "Lost Connection from Tcp Server", 'Reason:', reason)
del TcpServer.CLIENT_MAP[addr]
def dataReceived(self, tcp_data):
addr = self.transport.client # 获取客户端的连接信息
nowTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
try:
msg = tcp_data.decode("utf-8")
print("Received msg", msg, "from Tcp Client", addr)
time.sleep(5)
str = "来自服务器的响应 " + nowTime
self.transport.write(str.encode("utf-8"))
except BaseException as e:
print("Comd Execute Error from", addr, "data:", tcp_data)
str = "服务器发生异常 " + nowTime
self.transport.write(str.encode("utf-8"))好,Protocol类已经实现了,我们用他来创建工厂实例并启动TCP服务
port = 9527
serverFactory = Factory.forProtocol(TcpServer)
reactor.listenTCP(port, serverFactory)
print("#####", "Starting TCP Server on", port, "#####")
reactor.run()TCP服务成功启动,并且客户端连接上来以后也没有报错
D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\python.exe D:/MyWorkSpaces/projects/all/sample/python/twisted/server.py ##### Starting TCP Server on 9527 ##### connectedReceived msg 你好服务器,我是客户端 2019-08-10 11:13:09 from Tcp Client ('127.0.0.1', 3440)
有了服务器的经验,我们回来看reactor模块中IReactorTCP接口里的connectTCP方法,这个方法一共有5个参数
| 参数名 | 意义 | 默认值 |
|---|---|---|
| host | 服务器地址,IPv4或IPv6 | - |
| prot | 服务器端口 | - |
| factory | 客户端的工厂类,(根据注释中的提示:详情见twisted.internet包下的protocol模块中的ClientFactory类) | - |
| timeout | 连接超时时间,单位s | 30 |
| bindAddress | 本地的地址,格式为(host,port)的元祖 | None |
同样也很简单,我们只要以下两行代码就可以启动客户端了,但是和服务端类似,在这之前我们也需要实现一个Factory工厂类和Protocol协议类的实例
reactor.connectTCP(host, port, factory) reactor.run()
根据connectTCP方法的注释说明,我们直接可以找到ClientFactory类,类中有3个方法需要实现
ClientFactory.startedConnecting:未实现。开启连接时会调用该方法
ClientFactory.clientConnectionFailed:未实现。连接失败时会调用该方法
ClientFactory.clientConnectionLost:未实现。连接断开时会调用该方法
同样,为了简单,我们在startedConnecting方法中只做一下日志log的记录,在clientConnectionFailed和clientConnectionLost方法中记录入职log以后隔30s以后重试连接
class TcpClientFactory(ClientFactory):
def startedConnecting(self, connector):
print("Starting Connecting To Tcp Server", (connector.host, connector.port))
def clientConnectionLost(self, connector, reason):
print("Lost Connection from Tcp Server", (connector.host, connector.port), 'Reason:', reason)
time.sleep(30)
connector.connect()
def clientConnectionFailed(self, connector, reason):
print("Failed To Connect To Tcp Server", (connector.host, connector.port), 'Reason:', reason)
time.sleep(30)
connector.connect()启动TCP客户端,我们发现了和第一次启动服务端时一样的错误,这次我们有经验了,因为少了Protocol类的实现
------ File "D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\lib\site-packages\twisted\internet\selectreactor.py", line 149, in _doReadOrWrite why = getattr(selectable, method)() File "D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\lib\site-packages\twisted\internet\tcp.py", line 627, in doConnect self._connectDone() File "D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\lib\site-packages\twisted\internet\tcp.py", line 641, in _connectDone self.protocol = self.connector.buildProtocol(self.getPeer()) File "D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\lib\site-packages\twisted\internet\base.py", line 1157, in buildProtocol return self.factory.buildProtocol(addr) File "D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\lib\site-packages\twisted\internet\protocol.py", line 140, in buildProtocol p = self.protocol() builtins.TypeError: 'NoneType' object is not callable
和服务器端使用的是同一个Protocol父类,这里稍微做点和服务器端不同的事,实现connectionMade方法时我们往服务器端发送一条消息
class TcpClient(Protocol):
SERVER_MAP = {}
def connectionMade(self):
addr = self.transport.addr # 获取服务器端的连接信息
print("connected", self.transport.socket)
client_ip = addr[0]
TcpClient.SERVER_MAP[client_ip] = self
nowTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
str = "你好服务器,我是客户端 " + nowTime
self.transport.write(str.encode("utf-8")) # 向服务器发送信息
def connectionLost(self, reason):
addr = self.transport.addr # 获取服务器端的连接信息
client_ip = addr[0]
if client_ip in TcpClient.SERVER_MAP:
del TcpClient.SERVER_MAP[client_ip]
def dataReceived(self, tcp_data):
addr = self.transport.addr # 获取服务器端的连接信息
nowTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
try:
msg = tcp_data.decode("utf-8")
print("Received msg", msg, "from Tcp Server", addr)
time.sleep(5)
str = "来自客户端的响应 " + nowTime
self.transport.write(str.encode("utf-8"))
except BaseException as e:
print("Comd Execute Error from", addr, "data:", tcp_data)
str = "客户端发生异常 " + nowTime
self.transport.write(str.encode("utf-8"))因为在创建Factory类的时候和服务器端有些不一样,之前服务器端我们是通过Factory.forProtocol方法来实例化工厂对象的,而在客户端的时候我们是继承了Factory类的子类ClientFactory来实现的,所以我们需要重写buildProtocol方法来设置protocol实例
在TcpClientFactory类中重写buildProtocol方法:
class TcpClientFactory(ClientFactory):
def buildProtocol(self, addr):
print("Connected To Tcp Server", addr)
self.protocol = TcpClient()
return self.protocol然后用以下代码来启动TCP客户端:
host = "127.0.0.1" port = 9527 reactor.connectTCP(host, port, TcpClientFactory()) reactor.run()
TCP客户端成功启动,并且连接上服务器端以后也没有报错
D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\python.exe D:/MyWorkSpaces/projects/all/sample/python/twisted/client.py
Starting Connecting To Tcp Server ('127.0.0.1', 9527)
Connected To Tcp Server IPv4Address(type='TCP', host='127.0.0.1', port=9527)
connected
Received msg 来自服务器的响应 2019-08-10 11:57:42 from Tcp Server ('127.0.0.1', 9527) 上述就是小编为大家分享的python中怎么利用twisted实现TCP通讯了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注创新互联行业资讯频道。