新聞中心
這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)python中怎么利用twisted實(shí)現(xiàn)TCP通訊,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
創(chuàng)新互聯(lián)公司服務(wù)項(xiàng)目包括定海網(wǎng)站建設(shè)、定海網(wǎng)站制作、定海網(wǎng)頁制作以及定海網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,定海網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到定海省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
0.寫在前面
不管是服務(wù)器端還是客戶端,都是通過twisted
的reactor
來啟動(dòng)的,所以首先就需要導(dǎo)入twisted.internet
包下的reactor
模塊
從reactor
模塊的源碼中可以看出reactor
模塊其實(shí)是由多個(gè)接口組成的,并且提示了具體內(nèi)容需要查看twisted.internet
包下的interfaces
模塊中每個(gè)接口的具體注釋說明
當(dāng)然從reactor
模塊的注釋中也說明了twisted不止可以用于TCP服務(wù),而是提供了網(wǎng)絡(luò)方面的API、線程、調(diào)度等功能, 但是本次的實(shí)驗(yàn)僅僅測(cè)試一下TCP的服務(wù)器端和客戶端
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 }
1.TCP服務(wù)器端
·reactor反應(yīng)器
根據(jù)上面reactor
模塊的注釋,發(fā)現(xiàn)和TCP相關(guān)的需要查看interfaces
模塊下的IReactorTCP
接口,所以接下來我們移步至IReactorTCP
接口
IReactorTCP
接口中有兩個(gè)方法listenTCP
和connectTCP
,不管從方法名還是其說明都可以看出,前者是監(jiān)聽一個(gè)端口提供TCP服務(wù),后者是連接到服務(wù)端的TCP客戶端
def listenTCP(port, factory, backlog=50, interface=''): def connectTCP(host, port, factory, timeout=30, bindAddress=None):
所以要開啟一個(gè)TCP服務(wù)端,我們需要用到的是listenTCP
方法,這個(gè)方法有4個(gè)參數(shù)
參數(shù)名 | 意義 | 默認(rèn)值 |
---|---|---|
port | 監(jiān)聽的端口,也就是TCP服務(wù)啟動(dòng)的端口 | - |
factory | 服務(wù)端的工廠類(根據(jù)注釋中的提示:詳情見twisted.internet 包下的protocol 模塊中的ServerFactory 類) | - |
backlog | 監(jiān)聽隊(duì)列,響應(yīng)線程數(shù) | 50 |
interface | 要綁定到的本地IPv4或IPv6地址,默認(rèn)為空標(biāo)示所有IPv4的地址 | ‘’ |
·Factory工廠類
在設(shè)置了前兩個(gè)參數(shù)以后,然后通過下面兩行代碼就可以啟動(dòng)TCP服務(wù)了
reactor.listenTCP(port, ServerFactory()) reactor.run()
但是,此時(shí)啟動(dòng)的服務(wù)是有問題的,當(dāng)有客戶端連接到該服務(wù)的時(shí)候就會(huì)報(bào)錯(cuò)
------ 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ù)錯(cuò)誤提示,我們找到了問題的原因:
在客戶端連接到服務(wù)器端的時(shí)候,會(huì)調(diào)用Factory
類(ServerFactory
的父類)中的buildProtocol
方法來建立通訊協(xié)議(這里可以理解為客戶端和服務(wù)器端之間讀寫的實(shí)現(xiàn)方法),其中需要調(diào)用self.protocol
所指向的方法來初始化這個(gè)協(xié)議
然而此時(shí)的protocol
卻是None
,所以在協(xié)議的初始化階段出錯(cuò)了
·Protocol協(xié)議類
再次研究一下protocol
模塊中的Factory
類,發(fā)現(xiàn)類方法forProtocol
是用于創(chuàng)建factory
實(shí)例的,但是需要給定一個(gè)protocol
實(shí)例
很好,我們的目標(biāo)又近了一步,下面繼續(xù)研究Protocol
類
同樣在protocol
模塊中,我們找到了Protocol
類,他繼承自BaseProtocol
類,總共有下面幾個(gè)方法
BaseProtocol.makeConnection
:用于開啟連接,當(dāng)連接開啟后會(huì)回調(diào)connectionMade
方法BaseProtocol.connectionMade
:未實(shí)現(xiàn)。當(dāng)連接成功以后回調(diào)該方法Protocol.logPrefix
:返回當(dāng)前類的類名,用于日志logProtocol.dataReceived
:未實(shí)現(xiàn)。當(dāng)收到請(qǐng)求時(shí)被調(diào)用的方法Protocol.connectionLost
:未實(shí)現(xiàn)。當(dāng)連接斷開時(shí)調(diào)用的方法
所以現(xiàn)在我們只要繼承Protocol
類,寫一個(gè)自己的實(shí)現(xiàn)協(xié)議就可以了,并且只需要實(shí)現(xiàn)父類中未實(shí)現(xiàn)的3個(gè)方法
為了簡(jiǎn)單一些,在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"))
·啟動(dòng)TCP服務(wù)
好,Protocol
類已經(jīng)實(shí)現(xiàn)了,我們用他來創(chuàng)建工廠實(shí)例并啟動(dòng)TCP服務(wù)
port = 9527 serverFactory = Factory.forProtocol(TcpServer) reactor.listenTCP(port, serverFactory) print("#####", "Starting TCP Server on", port, "#####") reactor.run()
TCP服務(wù)成功啟動(dòng),并且客戶端連接上來以后也沒有報(bào)錯(cuò)
D:\MyWorkSpaces\tools\Anaconda3\envs\tf2\python.exe D:/MyWorkSpaces/projects/all/sample/python/twisted/server.py ##### Starting TCP Server on 9527 ##### connectedReceived msg 你好服務(wù)器,我是客戶端 2019-08-10 11:13:09 from Tcp Client ('127.0.0.1', 3440)
2.TCP客戶端
·reactor反應(yīng)器
有了服務(wù)器的經(jīng)驗(yàn),我們回來看reactor
模塊中IReactorTCP
接口里的connectTCP
方法,這個(gè)方法一共有5個(gè)參數(shù)
參數(shù)名 | 意義 | 默認(rèn)值 |
---|---|---|
host | 服務(wù)器地址,IPv4或IPv6 | - |
prot | 服務(wù)器端口 | - |
factory | 客戶端的工廠類,(根據(jù)注釋中的提示:詳情見twisted.internet 包下的protocol 模塊中的ClientFactory 類) | - |
timeout | 連接超時(shí)時(shí)間,單位s | 30 |
bindAddress | 本地的地址,格式為(host,port)的元祖 | None |
同樣也很簡(jiǎn)單,我們只要以下兩行代碼就可以啟動(dòng)客戶端了,但是和服務(wù)端類似,在這之前我們也需要實(shí)現(xiàn)一個(gè)Factory
工廠類和Protocol
協(xié)議類的實(shí)例
reactor.connectTCP(host, port, factory) reactor.run()
·Factory工廠類
根據(jù)connectTCP
方法的注釋說明,我們直接可以找到ClientFactory
類,類中有3個(gè)方法需要實(shí)現(xiàn)
ClientFactory.startedConnecting
:未實(shí)現(xiàn)。開啟連接時(shí)會(huì)調(diào)用該方法ClientFactory.clientConnectionFailed
:未實(shí)現(xiàn)。連接失敗時(shí)會(huì)調(diào)用該方法ClientFactory.clientConnectionLost
:未實(shí)現(xiàn)。連接斷開時(shí)會(huì)調(diào)用該方法
同樣,為了簡(jiǎn)單,我們?cè)?code>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()
啟動(dòng)TCP客戶端,我們發(fā)現(xiàn)了和第一次啟動(dòng)服務(wù)端時(shí)一樣的錯(cuò)誤,這次我們有經(jīng)驗(yàn)了,因?yàn)樯倭?code>Protocol類的實(shí)現(xiàn)
------ 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協(xié)議類
和服務(wù)器端使用的是同一個(gè)Protocol
父類,這里稍微做點(diǎn)和服務(wù)器端不同的事,實(shí)現(xiàn)connectionMade
方法時(shí)我們往服務(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"))
·啟動(dòng)TCP客戶端
因?yàn)樵趧?chuàng)建Factory
類的時(shí)候和服務(wù)器端有些不一樣,之前服務(wù)器端我們是通過Factory.forProtocol
方法來實(shí)例化工廠對(duì)象的,而在客戶端的時(shí)候我們是繼承了Factory
類的子類ClientFactory
來實(shí)現(xiàn)的,所以我們需要重寫buildProtocol
方法來設(shè)置protocol
實(shí)例
在TcpClientFactory
類中重寫buildProtocol
方法:
class TcpClientFactory(ClientFactory): def buildProtocol(self, addr): print("Connected To Tcp Server", addr) self.protocol = TcpClient() return self.protocol
然后用以下代碼來啟動(dòng)TCP客戶端:
host = "127.0.0.1" port = 9527 reactor.connectTCP(host, port, TcpClientFactory()) reactor.run()
TCP客戶端成功啟動(dòng),并且連接上服務(wù)器端以后也沒有報(bào)錯(cuò)
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) connectedReceived msg 來自服務(wù)器的響應(yīng) 2019-08-10 11:57:42 from Tcp Server ('127.0.0.1', 9527)
上述就是小編為大家分享的python中怎么利用twisted實(shí)現(xiàn)TCP通訊了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)頁名稱:python中怎么利用twisted實(shí)現(xiàn)TCP通訊
文章來源:http://fisionsoft.com.cn/article/gicgid.html