基於socketserver實現併發的socket編程

1、基於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()

2、基於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()

3、socketserver源碼分析

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

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

    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)3d

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

3.1 源碼總結

  • 基於tcp的socketserver咱們本身定義的類中的server

    • 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即客戶端地址
相關文章
相關標籤/搜索