遠程執行命令
類似遠程終端,輸入一個命令,在對端執行,也是網路通信編程的典型應用,
socket連接都是一樣的,發送內容也是一樣的,不同之處是,對端拿到的內容,當做命令執行,然后,將執行的結果反饋給對端,以windows系統為例,執行命令使用python中的subprocess模塊,

運行結果:

有一個問題:當運行的一個命令,其結果很長時,如ipconfig /all命令,其結果會很長,這時一次傳輸后,在接收端需要進行多次接收才能完成,如果像上面的客戶端,接收端最多一次接收1024位元組,然后又去回圈輸入命令,這時,接收結果時,就是接收的上一次沒有接收完的資訊,資訊出現錯亂,
解決方法,在服務器端先將結果進行統計,計算出長度,傳遞給客戶端,客戶端根據結果的長度,來回圈接收,直到將這一次的結果全部接收完成,

粘包現象:
在連續執行兩個發送命令:
conn.sendall(...)
conn.sendall(...)
正常是執行了兩次發送,但有時候,系統會因為兩次發送資料量很小,將兩次發送合并一次發送,這樣在接收端就會在一次接收中收到兩次的發送結果,造成錯誤,這就是粘包現象,
出現這種情況,在連續兩次發送中間增加一條命令進行隔斷,
檔案上傳:
實作思路,傳送端先要分析一條命令,判斷是上傳還是其他,上傳,要決議出上傳檔案的路徑、大小,然后回圈讀取,回圈發送;接收端要先接收傳送端的命令,分析是要接識訓是其他,接收,分析出要接收檔案的名稱,大小,然后開始回圈接收,回圈寫入檔案,

并發連接與socketserver模塊
前面的網路連接,雖然可以同時有多個客戶端連接到服務器端,但是只能有一個客戶端在同一時間與服務器端通信,之所以可以連接多個客戶端,是listen()引數決定的,即可以有幾個連接在排隊等候,這些連接只能等候前面的連接斷開后,才能與服務器進行通信,一次只能一個客戶端通信,這與實際需要不服,我們想能夠同時與多個客戶端進行通信,這時可以使用socketserver模塊實作,
服務器端:

客戶端:
客戶端可以啟動多個,同時連接到服務器端,同時發送資訊給服務器端,而且,服務器端可以同時接收到不同客戶端的資訊,可以向不同客戶端發送資訊,實作并發連接,
上述程式涉及的socketserver源代碼解讀:
先看下自定義的類MyServer,其繼承了socketserver.BaseRequestHandler,那就看一下這個類:

這個類,其物件有三個變數,request、client_address和server,request就是一個客戶端的連接,即conn,addr = server_socket.accept()中的conn,client_address就是客戶端的地址,包括ip和port,并且初始化后執行了setup()和handle()方法,最后執行finish()方法,這三個方法都是空的,沒有定義,需要我們進行覆寫,即重新進行定義,也是我們業務邏輯實作的地方,主要是在handle方法中,從MyServer定義類中看到,重寫了handle方法,
然后看程式的執行,先是生成ThreadingTCPServer物件,從前面的學習中我們知道,socketserver.ThreadingTCPServer(('192.168.1.117',9999),MyServer)就是執行構造方法__init__(),進入這個類:
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
類是空的,無構造方法,則繼續看其父類,先左后右,先看ThreadingMixIn,其也沒有定義構造方法,則從TCPServer父類中找:

可以看到,server_address引數就是我們傳遞的(‘192.168.1.117’,9999),RequestHandlerClass就是MyServer,即我們定義的類,構造方法先是執行了其父類,也就是BaseServer的構造方法,對變數進行賦值:

然后:
self.socket = socket.socket(self.address_family, self.socket_type)
這一句就很熟悉了,就是生成一個socket物件,使用的地址簇是AF_INET,即IPv4,協議是SOCK_STREAM,即TCP協議,
然后:
self.server_bind()
self.server_activate()
看這兩個方法:
一個是系結服務器的地址,一個是啟動監聽,到這一步,服務器就阻塞在了監聽這一步,等待客戶端連接的到來,同時,自定義的類MyServer被賦值給了變數self.RequestHandlerClass = RequestHandlerClass,
然后就到了下一步:server.serve_forever()
serve_forever()如下,其是在BaseServer類中定義的:

主要的邏輯是執行self.service_actions(),執行前有一個判斷,執行self._handle_request_noblock(),就是獲取有效的客戶端連接,

這里主要是執行self.process_request(request, client_address),那么這個方法是誰的方法呢?我們看當前類,即BaseServer中有process_request(request, client_address)方法:

但是,因為self是server,這個server是ThreadingTCPServer物件,所以要從這個類中找,按照查找順序,先找類本身,即ThreadingTCPServer,它沒有這個方法,然后查找父類,按先左后右,最先找到的是ThreadingMixIn中的process_reuqest,所以執行的應該是這個方法,

可以看到這個方法先生成一個新的執行緒,即t,這個執行緒引數target是proc_request_thread,即這個類中的這個方法,而這個方法主要執行的是self.finish_request(request, client_address),這里的self還是server,即ThreadingTCPServer物件,所以還要按照查找順序,查找這個finish_request方法,最后在BaseServer類中找到這個方法:

而這個方法是實體化RequestHandlerClass類,也就是我們自定義的類MyServer,于是進入MyServer類的實體化程序,即執行建構式,這個在一開始就分析過,其中呼叫了handle()方法,也就是執行我們定義的業務邏輯,
服務器端之所以能并發接收和發送,就是應為process_request()方法創建了新的執行緒,執行緒并發運行,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/356939.html
標籤:其他
上一篇:華為網路配置(DHCP)
下一篇:可靠資料傳輸原理詳細圖解
