網路遊戲資料傳輸:粘包的處理

Ymiku發表於2015-05-23
網路傳輸大體上包含這四個協議type area command message,這四層協議是依次向下傳遞的。
所謂協議,說簡單點就是伺服器端和客戶端的一個約定,
比如,向伺服器傳送(1,0,1,“賬號:*** 密碼***”)
約定type中1代表登入模組,因為登陸不需要經過area協議,所以跳過area協議,直接到command協議,
command中1代表登陸,2代表註冊,3代表返回登陸結果,傳入的是1,故進行登陸邏輯,注意,如果type不是1,此處1,2,3就會代表別的東西了。
如果登陸成功,伺服器會返回(1,0,3,“true”)
如果不嫌麻煩,完全可以只定義一個協議,但是估計沒人那麼做
在傳輸過程中,為了進行高效的傳輸,所有的資料都會連在一起,如果傳的是int a = 1 string b = “qaz”傳輸時會變成1qaz
為了進行區分,通常會在傳輸資料體前用一個8位元組的int傳入這個資料體的長度(一般一個int就佔8個位元組),這個int通常叫做資料頭
如果想傳的資料佔256個位元組,那麼傳的時候會傳送256+8個位元組
如果想傳送一個float和一個bool,那麼真正傳送的會是連在一起的int float int bool
下面說下如何進行解析
在連線伺服器後,用BeginReceive進行資料接收,其中一個引數定義了快取大小
建立一個位元組陣列byte[1024]來作為快取
建立一個ByteArray來儲存待處理資料,ByteArray是可以改變大小的
當快取中收到資料時,會將資料加入到ByteArray
之後,判斷是否正在進行資料處理,如果沒在則開始資料處理:





判斷ByteArray長度是否大於8,即是否把記錄訊息體長度的int讀取完
若小於8,不做處理等待,下次接受
若大於8,使用ReadInt讀取Int
ByteArray有一個position屬性,預設為0,當執行readint時會從0下標讀到8下標,因為int位元組長度為8,所以就算ByteArray裡有很多資料,就算有1024個位元組,readint也只會讀8位,之後position=8
舉個例子,如果傳入 1,2,3三個int 連續執行3次ReadInt就會得到1,2,3
獲取資料體長度int L後,再判斷剩下的資料長度是否大於L,若大於則說明資料完整,建立新的ArrayList2,從ArrayList讀取8到8+L的內容,AL的position+L
對ArrayList2進行Read,至於是ReadInt還是ReadBytes,要按照協議進行,簡而言之,伺服器傳的順序要和收的順序一致
之後,我們建立一個新的快取來儲存ListArray position之後的資料,再使ArrayList=這個新快取

再次執行"↓"之後的內容


相關程式碼:

 //訊息體長度為一個8位元組數值 長度不足的時候 說明訊息未接收完成 或者是廢棄訊息
        if (ioBuff.Length < 8) {
            isRead = false;
            return;
        }
        //讀取定義的訊息的長度
        int dataSize = ioBuff.ReadInt();
        if (dataSize > ioBuff.Length - 8) {
            //訊息體長度不夠 等待下個訊息的到來
            ioBuff.Postion = 0;
            isRead = false;
            return;
        }
        //建立完整訊息體的動態陣列
        ByteArray ioData = new ByteArray();
        //從訊息快取中取出正確的訊息體 位元組陣列內容
        ioData.WriteBytes(ioBuff.Buffer, 8, dataSize);
        ioBuff.Postion += dataSize;
        int type=ioData.ReadInt();
        int area=ioData.ReadInt();
        int command=ioData.ReadInt();
        byte[] cache=ioData.ReadBytes();
        object message = AceCode.Code.aceDecode(cache);
        //轉換為傳輸模型用於使用
        SocketModel model = new SocketModel(type, area, command, message);
        //將訊息儲存進訊息列表 等待Unity來讀取
        messageList.Add(model);
        ////
        //建立新快取區
        ByteArray bytes = new ByteArray();
        //將舊快取區的剩餘資料移動到新快取區
        bytes.WriteBytes(ioBuff.Buffer, ioBuff.Postion, ioBuff.Buffer.Length - ioBuff.Postion);
        //更新快取區
        ioBuff = bytes;
        onData();


相關文章