基於socketserver實現並發的socket編程


一、基於TCP協議

基於tcp的套接字,關鍵就是兩個循環,一個鏈接循環,一個通信循環

socketserver模塊中分兩大類:server類(解決鏈接問題)和request類(解決通信問題)

1.1 server類

126-基於socketserver實現並發的socket-server類.png?x-oss-process=style/watermark

1.2 request類

126-基於socketserver實現並發的socket-request類.png?x-oss-process=style/watermark

1.3 繼承關系

126-基於socketserver實現並發的socket-繼承關系1.png?x-oss-process=style/watermark

126-基於socketserver實現並發的socket-繼承關系2.png?x-oss-process=style/watermark

126-基於socketserver實現並發的socket-繼承關系3.png?x-oss-process=style/watermark

1.4 服務端

import socketserver


class MyHandler(socketserver.BaseRequestHandler):
    def handle(self):
        # 通信循環
        while True:
            # print(self.client_address)
            # print(self.request) #self.request=conn

            try:
                data = self.request.recv(1024)
                if len(data) == 0: break
                self.request.send(data.upper())
            except ConnectionResetError:
                break


if __name__ == '__main__':
    s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyHandler, bind_and_activate=True)

    s.serve_forever()  # 代表連接循環
    # 循環建立連接,每建立一個連接就會啟動一個線程(服務員)+調用Myhanlder類產生一個對象,調用該對象下的handle方法,專門與剛剛建立好的連接做通信循環

1.5 客戶端

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 8080))  # 指定服務端ip和端口

while True:
    # msg=input('>>: ').strip() #msg=''
    msg = 'client33333'  # msg=''
    if len(msg) == 0: continue
    phone.send(msg.encode('utf-8'))
    data = phone.recv(1024)
    print(data)

phone.close()

1.6 客戶端1

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 8080))  # 指定服務端ip和端口

while True:
    # msg=input('>>: ').strip() #msg=''
    msg = 'client11111'  # msg=''
    if len(msg) == 0: continue
    phone.send(msg.encode('utf-8'))
    data = phone.recv(1024)
    print(data)

phone.close()

二、基於UDP協議

2.1 服務端

import socketserver


class MyHandler(socketserver.BaseRequestHandler):
    def handle(self):
        # 通信循環
        print(self.client_address)
        print(self.request)

        data = self.request[0]
        print('客戶消息', data)
        self.request[1].sendto(data.upper(), self.client_address)


if __name__ == '__main__':
    s = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyHandler)
    s.serve_forever()

2.2 客戶端

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 數據報協議-》udp

while True:
    # msg=input('>>: ').strip() #msg=''
    msg = 'client1111'
    client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
    data, server_addr = client.recvfrom(1024)
    print(data)

client.close()

2.3 客戶端1

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 數據報協議-》udp

while True:
    # msg=input('>>: ').strip() #msg=''
    msg = 'client2222'
    client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
    data, server_addr = client.recvfrom(1024)
    print(data)

client.close()

三、socketserver源碼分析

ftpserver=socketserver.ThreadingTCPServer(('127.0.0.1', 8080),FtpServer)
ftpserver.serve_forever()
  • 查找屬性的順序:ThreadingTCPServer->ThreadingMixIn->TCPServer->BaseServer

    1. 實例化得到ftpserver,先找類ThreadingTCPServer的__init__,在TCPServer中找到,進而執行server_bind,server_active

    2. 找ftpserver下的serve_forever,在BaseServer中找到,進而執行self._handle_request_noblock(),該方法同樣是在BaseServer中

    3. 執行self._handle_request_noblock()進而執行request, client_address = self.get_request()(就是TCPServer中的self.socket.accept()),然后執行self.process_request(request, client_address)

    4. 在ThreadingMixIn中找到process_request,開啟多線程應對並發,進而執行process_request_thread,執行self.finish_request(request, client_address)

    5. 上述四部分完成了鏈接循環,本部分開始進入處理通訊部分,在BaseServer中找到finish_request,觸發我們自己定義的類的實例化,去找__init__方法,而我們自己定義的類沒有該方法,則去它的父類也就是BaseRequestHandler中找....

3.1 源碼總結

  • 基於tcp的socketserver我們自己定義的類中的

    • self.server即套接字對象

    • self.request即一個鏈接
        

    • self.client_address即客戶端地址

  • 基於udp的socketserver我們自己定義的類中的

    • self.request是一個元組(第一個元素是客戶端發來的數據,第二部分是服務端的udp套接字對象),如(b'adsf', <socket.socket fd=200, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
        
    • self.client_address即客戶端地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM