C++中TCP粘包分包處理


一  現象:

  粘包:

    A機器發出2包數據,B機器把2包數據作為一次收到,此時2包數據粘在一起。

  分包:

    A機器發送1包數據,B機器分為兩次收到這包數據,此時,這1報數據分為2次被B機器收到。

二  產生原因:

  當服務端和客戶端用到TCP通信時,可能會有以下場景(1)網絡有延遲、(2)客戶端頻繁發送的數據包給服務端,(3)TCP自身機制(需要等自己緩沖區滿后,才發送一包數據),由於這些原因會導致粘包,服務端一次收到的數據中包含多個數據包,此時就得分包處理數據。

三 處理粘包分包代碼:

  (1)協議:

 

  (2)代碼:

  

#include "stdafx.h"
#include"windows.h"

#include<iostream>

#define CHONG_QING_SIX_LOCK_FRAME_MAX_LENGTH        4096
#define CHONG_QING_SIX_LOCK_DATA_HEADER_LENGTH      6

int RecvData()
{
    DWORD recv_len = 0;
    int dataLength = 0;
    int sumDataLength = 0;
    int nRemainSize = 0;
    int lastPos = 0;
    BYTE recvbuf[CHONG_QING_SIX_LOCK_FRAME_MAX_LENGTH], databuf[CHONG_QING_SIX_LOCK_FRAME_MAX_LENGTH];
    char oneFrameData[1024];

    memset(recvbuf, 0, sizeof(recvbuf));        
    memset(databuf, 0, sizeof(databuf));        

    //收到服務端消息
    //接受數據,處理粘包,拆分包
    recv_len = (int)recv(m_Socket, (char *)recvbuf, CHONG_QING_SIX_LOCK_FRAME_MAX_LENGTH, 0);
    if (recv_len > 0)
    {
        memcpy(databuf + lastPos, recvbuf, recv_len);
        lastPos += recv_len;
        //判斷消息緩沖區的數據長度大於消息頭
        while (lastPos >= CHONG_QING_SIX_LOCK_DATA_HEADER_LENGTH)
        {
            //包頭做判斷,如果包頭錯誤,收到的數據全部清空
            if (databuf[0] == 0xEF && databuf[1] == 0xEF && databuf[2] == 0xEF && databuf[3] == 0xEF)
            {
                dataLength = MAKEWORD(databuf[4], databuf[5]);
                sumDataLength = CHONG_QING_SIX_LOCK_DATA_HEADER_LENGTH + dataLength + 6;
                //判斷消息緩沖區的數據長度大於消息體
                if (lastPos >= sumDataLength)
                {
                    //CRC校驗
                    if (CheckSum((byte *)databuf, dataLength + CHONG_QING_SIX_LOCK_DATA_HEADER_LENGTH + 2))
                    {
                        memcpy(oneFrameData, databuf, sumDataLength);
                        //處理數據
                        DealData(oneFrameData);
                        //剩余未處理消息緩沖區數據的長度
                        nRemainSize = lastPos - sumDataLength;
                        //將未處理的數據前移
                        if (nRemainSize > 0)
                        {
                            memcpy(databuf, databuf + (dataLength + CHONG_QING_SIX_LOCK_DATA_HEADER_LENGTH + 6), nRemainSize);
                            lastPos = nRemainSize;
                        }
                    }
                    else
                    {
                        if (nRemainSize > 0)
                        {
                            memcpy(databuf, databuf + sumDataLength, nRemainSize);
                        }

                        lastPos = nRemainSize;
                    }
                }
                else
                {
                    break;
                }
            }
            else      //尋找下一個包頭
            {
                BOOL isFind = FALSE;
                int nFindStart = 0;
                for (int k = 1; k < lastPos; k++)
                {
                    if (databuf[k] == 0xEF && databuf[k + 1] == 0xEF && databuf[k + 2] == 0xEF && databuf[k + 3] == 0xEF)
                    {
                        nFindStart = k;
                        isFind = TRUE;
                        break;
                    }
                }
                if (isFind == TRUE)
                {
                    memcpy(databuf, databuf + nFindStart, lastPos - nFindStart);

                    lastPos = lastPos - nFindStart;
                }
                else
                {
                    memset(databuf, 0, sizeof(databuf));
                    lastPos = 0;
                    break;
                }
            }
        }
    }
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{


    system("pause");
    return 0;
}

 

四   對以上代碼處理流程解釋:

  1)可以看到以上代碼分為2個緩沖區,第一個只負責接收數據,第二個負責處理數據。

  2)當服務端收到一包數據時,2種情況時,將處理數據的緩沖區的數據全部清空,(1)當出現包頭驗證錯誤,(2)CRC校驗失敗。

  3)當接受到的數據長度大於包頭,但是不大於整個消息體的長度時,保存前面接收的數據后,跳出while循環,繼續接受數據,

  4)當接收到的數據是多包數據時,它會一直在while循環里面處理數據,直到把每一包數據都分開並處理完后跳出。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM