Python32 Socket Server

類型

  • socketserver有幾種類型:

    class socketserver.TCPServer:用於TCP
    class socketserver.UDPServer:用於UDP
    class socketserver.UnixStreamServer:用於Unix的TCP
    class socketserver.UnixDatagramServer:用於Unix的UDP

image_1c5nj6qe0gc61p8p17phkk3sgm9.png-83.3kB
TCPServer繼承了BaseServer
UnixStreamServer繼承了TCPServer

  • 創建socket server至少分以下幾步:

    1、必須創建一個請求處理類,並且這個類要繼承BaseRequestHandler,還要重寫父類裏的handle方法(跟客戶端所有的交互都是在handle中寫的)。
    2、必須要實例化一個協議server(如TCPServer),並且傳遞server ip和你上面創建的請求處理類,給這個TCPServer(實例化的時候將IP和請求處理類傳給TCPServer)。
    3、然後可以根據上面的實例來處理請求:
    server.handle_request() #只處理一個請求
    server.handle_forever() #處理多個請求,永遠執行(一般都是用這個)
    4、調用server_close()去關閉

server端:

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    '''第1步:創建處理類,繼承Base。 客戶端每一次請求過來都會實例化這個類'''

    def handle(self):
        '''第1步:重寫handle方法,handle默認存在父類中代碼是空的。 客戶端所有交互都在handle中完成'''

        while True: #使其可以循環發送數據
            self.data = self.request.recv(1024).decode().strip()
            #這裏的self.request.recv相當於之前用的conn.recv

            print ("{} wrote:".format(self.client_address[0]))
            #打印客戶端的IP地址信息

            print (self.data)
            #打印數據信息

            self.request.send(self.data.upper().encode('utf-8'))
            #傳回數據給客戶端,只是upper了一下
            #sendall就是重複調用send

if __name__ == "__main__":
    HOST,PORT = "localhost",9999

    server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
    #第2步:實例化TCPServer,並將IP和MyTCPHandler當做參數傳給請求處理類
    #監聽客戶端的每一個請求,就會實例化MyTCPHandler這個類,拿MyTCPHandler的handle與客戶端交互。

    server.serve_forever()
    #第3步:允許永遠處理多個請求

client端:

import socket

client = socket.socket()
client.connect(('localhost',9999))

while True:

    msg = input(">>:").strip()
    if len(msg) == 0:continue
    client.send(msg.encode('utf-8'))
    data = client.recv(1024)
    print ("recv:",data.decode())

client.close()

client執行結果:
>>:abc
recv: ABC
>>:efg
recv: EFG
>>:hhh
recv: HHH
>>:

server執行結果:
127.0.0.1 wrote:
abc
127.0.0.1 wrote:
efg
127.0.0.1 wrote:
hhh

client斷開後server報錯:
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 53933)
Traceback (most recent call last):
  File "D:\python37\lib\socketserver.py", line 313, in _handle_request_noblock
    self.process_request(request, client_address)
  File "D:\python37\lib\socketserver.py", line 344, in process_request
    self.finish_request(request, client_address)
  File "D:\python37\lib\socketserver.py", line 357, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "D:\python37\lib\socketserver.py", line 712, in __init__
    self.handle()
  File "E:/python/代碼練習/A2.py", line 11, in handle
    self.data = self.request.recv(1024).decode().strip()
ConnectionResetError: [WinError 10054] 遠程主機強迫關閉了一個現有的連接。
----------------------------------------
#可以看到這裏報錯ConnectionResetError: [WinError 10054]
修改server端:
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):

    def handle(self):

        while True: #使其可以循環發送數據
           try: #使用try的話就不需要使用 if not data的方式來判斷客戶端斷開時,無數據的情況了
                self.data = self.request.recv(1024).decode().strip()
                #這裏的self.request.recv相當於之前用的conn.recv

                print ("{} wrote:".format(self.client_address[0]))

                print (self.data)

                self.request.send(self.data.upper().encode('utf-8'))
           except ConnectionResetError as e:
               print ("err:",e)
               break    #這裏一定要break,不然就會一直死循環

if __name__ == "__main__":
    HOST,PORT = "localhost",9999

    server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)

    server.serve_forever()

server執行結果:
127.0.0.1 wrote:
abc
127.0.0.1 wrote:
123
err: [WinError 10054] 遠程主機強迫關閉了一個現有的連接。
#客戶端斷開連接後,就通過斷言的方式抓到錯誤了。

上面的代碼目前還不能支持多併發,如果有多個併發,後面的併發就會被掛起; 如果要併發的話,需要修改一下代碼。

image_1c5pi0sn51denhou1t7b14k91tlcm.png-5.3kB
通過ctrl點TCPServer
image_1c5pi2lgi1gj1qgo18hn8a01th013.png-4.3kB
可以看到TCPServer是繼承了BaseServer
image_1c5pi3dgaqh51lvc1co817q5vn71g.png-3.8kB

修改server端:

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):

    def handle(self):

        while True: #使其可以循環發送數據
           try: #使用try的話就不需要使用 if not data的方式來判斷客戶端斷開時,無數據的情況了
                self.data = self.request.recv(1024).decode().strip()

                print ("{} wrote:".format(self.client_address[0]))

                print (self.data)

                self.request.send(self.data.upper().encode('utf-8'))

           except ConnectionResetError as e:
               print ("err:",e)
               break    
if __name__ == "__main__":
    HOST,PORT = "localhost",9999

    server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)
    # 上面的代碼需要使用ThreadingTCPServer

    server.serve_forever()

client 1執行結果:
>>:test 1
recv: TEST 1
>>:

client 2執行結果:
>>:test 2
recv: TEST 2
>>:

client 3執行結果:
>>:test 3
recv: TEST 3
>>:

server 執行結果:
127.0.0.1 wrote:
test 1
127.0.0.1 wrote:
test 2
127.0.0.1 wrote:
test 3

image_1c5phqhfeomh1fbhptttajvl29.png-16.4kB
可以看到server端現在支持多併發,沒有被掛起;每來一個請求會開啓一個新線程與server交互;每個線程都是獨立的,10個線程,就可以做10件事情。

通過ctrl點ThreadingTCPServer
image_1c5piagcm85rqmh56f1a2l161v1t.png-12.3kB
可以看到將TCPServer這個類傳了進去
同時還傳了ThreadingMixIn;TCPServer是負責與客戶端交互,而多線程都是由ThreadingMixIn實現的。

ctrl點ThreadingMixIn
image_1c5pigho4pp5tcd6adtt1btr2a.png-30.6kB
這部分就是多線程的主要代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章