文件上傳下載、socketserver(併發)、解讀socketserver源碼

一、文件上傳/下載python

  學習了socket套接字,咱們如今能夠寫一個文件上傳/下載的程序,以下示例:算法

 

 

  分析上邊代碼,咱們發現,client發送上傳文件相關信息的字典序列化以後,server又給client發送了一個狀態碼,而後client纔開始上傳文件數據,思考一下若是不發送狀態碼,直接發送文件數據且避免黏包現象發生該怎麼寫?沒錯!能夠用struct模塊解決,以下示例:併發

  struct(四chua科特)打包如何用呢,簡單寫一下:socket

import struct
num = 156
#將int類型的數據打包成4個字節的數據
num_stru = struct.pack('i',num)                    #pack(爬可)  
print(len(num_stru))          #他的長度四個字節
print(num_stru)              
print('11111111111111111111111111111111')

#在經過int類型解包,將前面打包的數據解包成打包以前的int數據
num2 = struct.unpack('i',num_stru) #解包出來是個元組        unpack(嗯牌可)
print(num2)#(156,)
print(num2[0])

  服務端代碼:tcp

 

  客戶端代碼:學習

 

 

  分析上面的代碼,看看是如何避免黏包現象的?client先給server發送字典的長度的pack包,再發送字典,再發送文件數據,server先接收4字節的pack包,進行unpack後獲得字典長度,再根據字典長度接收字典,最後再循環接收文件數據。而且該示例還進行了MD5算法來進行文件一致性校驗。線程

2、socketserver(併發)(騷k特點我)3d

  經過這兩天學習socket套接字,咱們發如今寫服務端和客戶端的時候,在創建鏈接以前老是要寫一些固定的重複代碼來,好比socket.socket()(建立套接字對象)、bind()、acecept()等等,學習了socketserver(併發)以後,就能夠少寫一些代碼,而且實現併發,以下示例:code

 服務端:server

import socketserver                            

#1 定義一個類
class MyServer(socketserver.BaseRequestHandler): #2 類裏面繼承socketserver.BaseRequestHandler  #BaseRequestHandler貝斯蕊快四特漢得了
    # 3 類裏面定義一個handle方法,handle名稱不能變
    def handle(self):    #handle(漢得了)
        while 1:
        # self.request      #conn連接通道                              request(蕊快死特)
            from_client_data = self.request.recv(1024).decode('utf-8')
            print(from_client_data)
            server_input = input('明巍sb說>>>')
            self.request.send(server_input.encode('utf-8'))
        # self.request.close()
if __name__ == '__main__':
    #服務端的IP地址和端口
    ip_port = ('127.0.0.1',8001)
    socketserver.TCPServer.allow_reuse_address = True    #容許地址重用
    #綁定IP地址和端口,而且啓動我定義的上面這個類
    server = socketserver.ThreadingTCPServer(ip_port,MyServer)
    #永久的給我執行下去
    server.serve_forever()

客戶端:

import socket

tcp_client = socket.socket()
server_ip_port = ('127.0.0.1',8001)
tcp_client.connect(server_ip_port)
while 1:
    client_msg = input('大陽哥>>>')
    tcp_client.send(client_msg.encode('utf-8'))
    from_server_msg = tcp_client.recv(1024).decode('utf-8')
    print(from_server_msg)

3、解讀python中socketserver源碼

  結合下圖中類的繼承關係和類中的方法,分析socketserver代碼的執行過程:

  a、初始化相關過程:server = socketserver.ThreadingTCPServer(('127.0.0.1',8800),Myserver)

    (1)TCPServer類中的__init__方法:

                     TCPServer類中主動執行BaseServer類中的__init__ 方法(把本身建立的Myserver類傳參);

                     建立socket.socket()對象;

                     server_bind() -- 在TCPServer類中執行了socket.bind(self.server_address)

                     server_activate() -- 在TCPServer類中執行了socket.listen(5)

    (2)BaseServer類中的__init__ 方法:

      將參數server_address(ip地址和端口號)賦值給了self.server_address;

                     將形參RequestHandlerClass(實參是咱們本身建立的Myserver類)賦值給了self.RequestHandlerClass;

 

  b、執行serve_forever的相關代碼:

    (1)執行BaseServer類中的serve_forever()方法:

      注意看if ready後邊的那句self._handle_request_noblock(),其餘先忽略;

    (2)執行BaseServer中的_handle_request_noblock(self)方法:

      看第一個try中request,client_address = self.get_request(),

        TCPServer中有get_request()方法,方法中是return self.socket.accept(),即等待鏈接;

                     再看第二個try中的self.process_request(request,client_address)

     (3)鏈接成功以後拿到了request(即conn)和client_address(即addr)再去執行BaseServer類中的.process_request方法;

        建立線程,執行方法process_request_thread()

 

    (4)執行ThreadingMixIn 類中的process_request_thread(self, request, client_address)方法:

                     看try中self.finish_request(request,client_address)

    (5)執行BaseServer中的finish_request(request,client_address)方法:

      此時還記得RequestHandlerClass這個參數嗎?正是咱們執行BaseServer中__init__方法時傳過來的本身建立的類Myserver,類() 即實例化一個Myserver對象,而且傳了兩個參數(conn,addr),可是咱們本身寫的Myserver類中沒有__init__方法,因此執行Myserver父類BaseRequestHandler類中的__init__方法,而且帶了每一個鏈接的線程的conn和addr:

     (6)執行BaseRequestHandler中的__init__方法:

      此時self是Myserver類的對象,因此優先去執行Myserver類中handle方法。

 附錄:如下是各種的繼承關係以及類中成員方法:

   

 

 

 

相關文章
相關標籤/搜索