溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

python中怎么利用twisted實現(xiàn)TCP通訊

發(fā)布時間:2021-07-02 15:19:45 來源:億速云 閱讀:640 作者:Leah 欄目:大數(shù)據(jù)

這期內(nèi)容當中小編將會給大家?guī)碛嘘P(guān)python中怎么利用twisted實現(xiàn)TCP通訊,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

0.寫在前面

不管是服務(wù)器端還是客戶端,都是通過twistedreactor來啟動的,所以首先就需要導(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>}

1.TCP服務(wù)器端

·reactor反應(yīng)器

根據(jù)上面reactor模塊的注釋,發(fā)現(xiàn)和TCP相關(guān)的需要查看interfaces模塊下的IReactorTCP接口,所以接下來我們移步至IReactorTCP接口

IReactorTCP接口中有兩個方法listenTCPconnectTCP,不管從方法名還是其說明都可以看出,前者是監(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的地址‘’

·Factory工廠類

在設(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協(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個方法

為了簡單一些,在connectionMadeconnectionLost方法中我們只記錄一下客戶端的連接信息并輸出一下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"))

·啟動TCP服務(wù)

好,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)

2.TCP客戶端

·reactor反應(yīng)器

有了服務(wù)器的經(jīng)驗,我們回來看reactor模塊中IReactorTCP接口里的connectTCP方法,這個方法一共有5個參數(shù)

參數(shù)名意義默認值
host服務(wù)器地址,IPv4或IPv6-
prot服務(wù)器端口-
factory客戶端的工廠類,(根據(jù)注釋中的提示:詳情見twisted.internet包下的protocol模塊中的ClientFactory類)-
timeout連接超時時間,單位s30
bindAddress本地的地址,格式為(host,port)的元祖None

同樣也很簡單,我們只要以下兩行代碼就可以啟動客戶端了,但是和服務(wù)端類似,在這之前我們也需要實現(xiàn)一個Factory工廠類和Protocol協(xié)議類的實例

reactor.connectTCP(host, port, factory)
reactor.run()

·Factory工廠類

根據(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的記錄,在clientConnectionFailedclientConnectionLost方法中記錄入職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

·Protocol協(xié)議類

和服務(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"))

·啟動TCP客戶端

因為在創(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è)資訊頻道。

向AI問一下細節(jié)

免責(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)容。

AI