• 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循环里面处理数据,直到把每一包数据都分开并处理完后跳出。

    111
  • 相关阅读:
    python读取配置文件
    日志截取
    QJsonObject
    OpenStack
    生成器迭代器正则视频笔记
    使用Qt.createComponent 动态加载组件
    linux 远程执行命令
    Django 建立工程连接数据库
    arm基础1
    QSetting的用法
  • 原文地址:https://www.cnblogs.com/zwj-199306231519/p/13699982.html
Copyright © 2020-2023  润新知