您好,登錄后才能下訂單哦!
這期內(nèi)容當中小編將會給大家?guī)碛嘘P(guān)python中怎么利用twisted實現(xiàn)TCP通訊,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
0.寫在前面
不管是服務(wù)器端還是客戶端,都是通過twisted
的reactor
來啟動的,所以首先就需要導(dǎo)入twisted.internet
包下的reactor
模塊
從reactor
模塊的源碼中可以看出reactor
模塊其實是由多個接口組成的,并且提示了具體內(nèi)容需要查看twisted.internet
包下的interfaces
模塊中每個接口的具體注釋說明
當然從reactor
模塊的注釋中也說明了twisted不止可以用于TCP服務(wù),而是提供了網(wǎng)絡(luò)方面的API、線程、調(diào)度等功能, 但是本次的實驗僅僅測試一下TCP的服務(wù)器端和客戶端
reactor注釋中提到的支持的接口: @see: L{IReactorCore<twisted.internet.interfaces.IReactorCore>} @see: L{IReactorTime<twisted.internet.interfaces.IReactorTime>} @see: L{IReactorProcess<twisted.internet.interfaces.IReactorProcess>} @see: L{IReactorTCP<twisted.internet.interfaces.IReactorTCP>} @see: L{IReactorSSL<twisted.internet.interfaces.IReactorSSL>} @see: L{IReactorUDP<twisted.internet.interfaces.IReactorUDP>} @see: L{IReactorMulticast<twisted.internet.interfaces.IReactorMulticast>} @see: L{IReactorUNIX<twisted.internet.interfaces.IReactorUNIX>} @see: L{IReactorUNIXDatagram<twisted.internet.interfaces.IReactorUNIXDatagram>} @see: L{IReactorFDSet<twisted.internet.interfaces.IReactorFDSet>} @see: L{IReactorThreads<twisted.internet.interfaces.IReactorThreads>} @see: L{IReactorPluggableResolver<twisted.internet.interfaces.IReactorPluggableResolver>}
根據(jù)上面reactor
模塊的注釋,發(fā)現(xiàn)和TCP相關(guān)的需要查看interfaces
模塊下的IReactorTCP
接口,所以接下來我們移步至IReactorTCP
接口
IReactorTCP
接口中有兩個方法listenTCP
和connectTCP
,不管從方法名還是其說明都可以看出,前者是監(jiān)聽一個端口提供TCP服務(wù),后者是連接到服務(wù)端的TCP客戶端
def listenTCP(port, factory, backlog=50, interface=''): def connectTCP(host, port, factory, timeout=30, bindAddress=None):
所以要開啟一個TCP服務(wù)端,我們需要用到的是listenTCP
方法,這個方法有4個參數(shù)
參數(shù)名 | 意義 | 默認值 |
---|---|---|
port | 監(jiān)聽的端口,也就是TCP服務(wù)啟動的端口 | - |
factory | 服務(wù)端的工廠類(根據(jù)注釋中的提示:詳情見twisted.internet 包下的protocol 模塊中的ServerFactory 類) | - |
backlog | 監(jiān)聽隊列,響應(yīng)線程數(shù) | 50 |
interface | 要綁定到的本地IPv4或IPv6地址,默認為空標示所有IPv4的地址 | ‘’ |
在設(shè)置了前兩個參數(shù)以后,然后通過下面兩行代碼就可以啟動TCP服務(wù)了
reactor.listenTCP(port, ServerFactory()) reactor.run()
但是,此時啟動的服務(wù)是有問題的,當有客戶端連接到該服務(wù)的時候就會報錯
--- <exception caught here> --- 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
根據(jù)錯誤提示,我們找到了問題的原因:
在客戶端連接到服務(wù)器端的時候,會調(diào)用Factory
類(ServerFactory
的父類)中的buildProtocol
方法來建立通訊協(xié)議(這里可以理解為客戶端和服務(wù)器端之間讀寫的實現(xiàn)方法),其中需要調(diào)用self.protocol
所指向的方法來初始化這個協(xié)議
然而此時的protocol
卻是None
,所以在協(xié)議的初始化階段出錯了
再次研究一下protocol
模塊中的Factory
類,發(fā)現(xiàn)類方法forProtocol
是用于創(chuàng)建factory
實例的,但是需要給定一個protocol
實例
很好,我們的目標又近了一步,下面繼續(xù)研究Protocol
類
同樣在protocol
模塊中,我們找到了Protocol
類,他繼承自BaseProtocol
類,總共有下面幾個方法
BaseProtocol.makeConnection
:用于開啟連接,當連接開啟后會回調(diào)connectionMade
方法
BaseProtocol.connectionMade
:未實現(xiàn)。當連接成功以后回調(diào)該方法
Protocol.logPrefix
:返回當前類的類名,用于日志log
Protocol.dataReceived
:未實現(xiàn)。當收到請求時被調(diào)用的方法
Protocol.connectionLost
:未實現(xiàn)。當連接斷開時調(diào)用的方法
所以現(xiàn)在我們只要繼承Protocol
類,寫一個自己的實現(xiàn)協(xié)議就可以了,并且只需要實現(xiàn)父類中未實現(xiàn)的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 = "來自服務(wù)器的響應(yīng) " + nowTime self.transport.write(str.encode("utf-8")) except BaseException as e: print("Comd Execute Error from", addr, "data:", tcp_data) str = "服務(wù)器發(fā)生異常 " + nowTime self.transport.write(str.encode("utf-8"))
好,Protocol
類已經(jīng)實現(xiàn)了,我們用他來創(chuàng)建工廠實例并啟動TCP服務(wù)
port = 9527 serverFactory = Factory.forProtocol(TcpServer) reactor.listenTCP(port, serverFactory) print("#####", "Starting TCP Server on", port, "#####") reactor.run()
TCP服務(wù)成功啟動,并且客戶端連接上來以后也沒有報錯
D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\python.exe D:/MyWorkSpaces/projects/all/sample/python/twisted/server.py ##### Starting TCP Server on 9527 ##### connected <socket.socket fd=824, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9527), raddr=('127.0.0.1', 3440)> Received msg 你好服務(wù)器,我是客戶端 2019-08-10 11:13:09 from Tcp Client ('127.0.0.1', 3440)
有了服務(wù)器的經(jīng)驗,我們回來看reactor
模塊中IReactorTCP
接口里的connectTCP
方法,這個方法一共有5個參數(shù)
參數(shù)名 | 意義 | 默認值 |
---|---|---|
host | 服務(wù)器地址,IPv4或IPv6 | - |
prot | 服務(wù)器端口 | - |
factory | 客戶端的工廠類,(根據(jù)注釋中的提示:詳情見twisted.internet 包下的protocol 模塊中的ClientFactory 類) | - |
timeout | 連接超時時間,單位s | 30 |
bindAddress | 本地的地址,格式為(host,port)的元祖 | None |
同樣也很簡單,我們只要以下兩行代碼就可以啟動客戶端了,但是和服務(wù)端類似,在這之前我們也需要實現(xiàn)一個Factory
工廠類和Protocol
協(xié)議類的實例
reactor.connectTCP(host, port, factory) reactor.run()
根據(jù)connectTCP
方法的注釋說明,我們直接可以找到ClientFactory
類,類中有3個方法需要實現(xiàn)
ClientFactory.startedConnecting
:未實現(xiàn)。開啟連接時會調(diào)用該方法
ClientFactory.clientConnectionFailed
:未實現(xiàn)。連接失敗時會調(diào)用該方法
ClientFactory.clientConnectionLost
:未實現(xiàn)。連接斷開時會調(diào)用該方法
同樣,為了簡單,我們在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客戶端,我們發(fā)現(xiàn)了和第一次啟動服務(wù)端時一樣的錯誤,這次我們有經(jīng)驗了,因為少了Protocol
類的實現(xiàn)
--- <exception caught here> --- 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
和服務(wù)器端使用的是同一個Protocol
父類,這里稍微做點和服務(wù)器端不同的事,實現(xiàn)connectionMade
方法時我們往服務(wù)器端發(fā)送一條消息
class TcpClient(Protocol): SERVER_MAP = {} def connectionMade(self): addr = self.transport.addr # 獲取服務(wù)器端的連接信息 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 = "你好服務(wù)器,我是客戶端 " + nowTime self.transport.write(str.encode("utf-8")) # 向服務(wù)器發(fā)送信息 def connectionLost(self, reason): addr = self.transport.addr # 獲取服務(wù)器端的連接信息 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 # 獲取服務(wù)器端的連接信息 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 = "來自客戶端的響應(yīng) " + nowTime self.transport.write(str.encode("utf-8")) except BaseException as e: print("Comd Execute Error from", addr, "data:", tcp_data) str = "客戶端發(fā)生異常 " + nowTime self.transport.write(str.encode("utf-8"))
因為在創(chuàng)建Factory
類的時候和服務(wù)器端有些不一樣,之前服務(wù)器端我們是通過Factory.forProtocol
方法來實例化工廠對象的,而在客戶端的時候我們是繼承了Factory
類的子類ClientFactory
來實現(xiàn)的,所以我們需要重寫buildProtocol
方法來設(shè)置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客戶端成功啟動,并且連接上服務(wù)器端以后也沒有報錯
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 <socket.socket fd=452, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 3770), raddr=('127.0.0.1', 9527)> Received msg 來自服務(wù)器的響應(yīng) 2019-08-10 11:57:42 from Tcp Server ('127.0.0.1', 9527)
上述就是小編為大家分享的python中怎么利用twisted實現(xiàn)TCP通訊了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。