1. 程式人生 > >粘包問題及解決

粘包問題及解決

1>先模擬ssh,編寫一個遠端執行命令的程式

    其實也就是客戶端輸入命令,再由服務端執行並返回結果的一個過程,基於之前的簡單通訊指令碼,改一下,如下


    當服務端收取命令之後,用subpcocess模組執行命令,並且把執行結果返回給客戶端,客戶端解碼展示,客戶端

    的程式碼及執行結果如下


2>粘包問題

    客戶端更改接收字串位數的上限,200,並且再傳遞一個新的命令,如‘檢視C盤的檔案’,但是注意返回結果,並不是當     前命令的結果,而是上一條命令的剩餘部分!


理解TCP也稱‘流式協議’,其實這個問題也很好理解,因服務端這邊給客戶端傳送的資料肯定超過200位元組,

而客戶端這邊這一次只收取200位元組,所以,沒取完的剩餘部分依然存留在IO緩衝區(也可以理解為服務端

通往客戶端的管道)裡,等到客戶端下一次來取值,所以,客戶端再發送另外一個命令,服務端這邊執行並把

新的結果再從管道傳送給客戶端,客戶端取值就從上次的剩餘部分開始了。

這就是-------粘包現象

粘包問題只存在於TCP,not UDP

3>粘包問題解析

    看一下客戶端的‘粘包現象’,進行TCP協議通訊時,,會啟用

    一種優化方法(Nagle演算法),將資料量小且傳送間隔小的資料包合併成一個大的資料包傳送,(這樣可以節省網路IO

    ,減少延遲),所以如下所示,客戶端傳送了兩次資料,但是服務端卻一次就收完了,因為在傳送時就合併了,所以服務端

    的第二次收取,沒有任何資料。

    

    記住一點:客戶端的傳送與服務端的接收並不是一對一的關係,他們實質上都是把資料拷貝只各自的作業系統,

    至於作業系統怎麼發,何時發,應用程式就根本管不到了。

4>解決粘包問題

    既然產生這個問題的原因就是----客戶端設定的接收值小於實際值,其實也就是客戶端並不知道服務端究竟會傳多少資料

    過來(因為就是把接收的最大值寫成無限大,也有超過的可能),所以,可以這樣解決:傳送端在傳送資料前,把自己將

    要傳送的位元組流總大小讓接收端知曉,然後接收端來一個死迴圈接收完所有資料。

    解決這個問題之前,先介紹一個內建的庫,struck

    

    如上,不管原始資料大小(應該也有個範圍),它的pack方法都能轉換成一個4位長度的Bytes型別資料,然後再通過

    unpack解開,得到一個元祖型別的資料格式,第一個就是真實的值,所以,可以把個值加在真實資料的前面(頭部),

    就當做資料的描述資訊,告知對方我的資料大小,對方收到後,先取錢4個位元組,解碼得到資料大小,這樣,知道了

    資料的大小,就知道如何收取資料了。

    步驟拆解,如下服務端的寫法(注意下面的send,兩次雖然分開寫了,但是他們會組裝到一起傳送,nagle演算法)


    客戶端的寫法


    這樣寫其實也可以。只是也有 total_size[0] 超出接收範圍的情況。


5>更高階的解決粘包問題的方法

    上面的方法始終都有資料超出範圍的風險,接下來設計一個字典型別的頭部資訊,讓頭部資訊能包含更多資料,也更完美

    的解決 資料範圍這個問題,

    因為始終還是在服務端返回資料問題,所以還是從服務端開始分析,優化,分解步驟

    思路:服務端步驟: 1>接收命令,2>執行命令,3>【注意注意:這裡並不是傳送結果也不是傳送頭大小】,而是先

    設計一個dic型別的頭資訊,裡面至少應該包含真實檔案的大小,再發送這個頭資訊的固定長度,4>傳送頭部資訊,

    5>傳送真實資料。

               客戶端步驟:1>傳送命令,2>接收頭部資訊大小,3>根據頭部資訊大小接收頭部資訊,4>根據頭部資訊中真實

     資料大小,接收真實資料。(這裡的2,3,4步就是對應服務端的3,4,5)

    服務端程式碼如下


客戶端