C++面试5_ TCP 粘包2(工业级)

复制代码
//接收端
#pragma pack(1)
struct protocol_head_
{
    int MainMsgID; // 主消息ID
    int SubMsgID; // 子消息ID
    int DataLen; // 数据长度,不包含头
    int Reserve1; // 预留字段1
};
复制代码
void Test::recvData()
{
    LOG_DEBUGGING("启动接收数据线程");
    int headLen = sizeof(protocol_head_);
    char *pHeadData = new char[headLen];
    char *pData = nullptr;
    int npDataLen = 0;
    while (m_bInited)
    {
        if (!m_bConnected)
        {
            Sleep(1);
            continue;
        }
        int nRecvLen = 0;
        memset(pHeadData, 0, headLen);

        // 读取头数据,先收固定长度包头
        while (1)
        {

            int len = ::recv(m_s, pHeadData + nRecvLen, headLen - nRecvLen, 0);

            if (len <= 0)
            {// 连接异常
                m_bConnected = false;
                break;
            }
            else
            {
                nRecvLen += len;

                if (nRecvLen >= headLen)
                {
                    break;
                }
            }
        }
        if (!m_bConnected) continue;

        nRecvLen = 0;

        protocol_head_ *pHead = (protocol_head_*)pHeadData;

        // 读取消息体,(从包头取长度)
        int bodyLen = pHead->DataLen;

        if (bodyLen < 0)
        {
            m_bConnected = false;
            continue;
        }
        if (bodyLen == 0)
        {
            continue;
        }
        else
        {
            if (pData == nullptr || headLen + bodyLen > npDataLen)
            {
                if (pData != nullptr) delete[]pData;
                pData = new char[headLen + bodyLen];
                npDataLen = headLen + bodyLen;
            }
            memcpy(pData, pHeadData, headLen);

             //再按长度收包体
            while (1)
            {
                int len = ::recv(m_s, pData + nRecvLen + headLen, bodyLen - nRecvLen, 0);
                if (len <= 0)
                {// 连接异常
                    m_bConnected = false;
                    break;
                }
                else
                {
                    nRecvLen += len;

                    if (nRecvLen >= bodyLen)
                    {
                        break;
                    }
                }
            }
            if (!m_bConnected) continue;
        }

//一包一包处理

if (pHead->MainMsgID == N_NOTIFY && pHead->SubMsgID == N_DACTYLOGRAM__INFO)

{

}

}

void Test:recvData()

{

LOG_DEBUGGING("启动接收数据线程(buffer模型)");

const int HEAD_LEN = sizeof(protocol_head_);

const int MAX_PACKET_SIZE = 1024 * 1024; // 1MB(根据业务调)

const int MAX_BUFFER_SIZE = 2 * 1024 * 1024;

std::vector<char> buffer;

buffer.reserve(64 * 1024); // 预留64KB,减少扩容

char tmp4096;

while (m_bInited)

{

if (!m_bConnected)

{

Sleep(1);

continue;

}

// ================== 1️⃣ 收数据 ==================

int len = recv(m_s, tmp, sizeof(tmp), 0);

if (len == 0)

{

LOG_DEBUGGING("socket closed by peer");

m_bConnected = false;

continue;

}

else if (len < 0)

{

int err = WSAGetLastError();

if (err == WSAEWOULDBLOCK)

{

Sleep(1);

continue;

}

LOG_DEBUGGING("recv error: %d", err);

m_bConnected = false;

continue;

}

// 写入 buffer

buffer.insert(buffer.end(), tmp, tmp + len);

// 防止buffer无限增长

if (buffer.size() > MAX_BUFFER_SIZE)

{

LOG_DEBUGGING("buffer overflow, reset connection");

m_bConnected = false;

buffer.clear();

continue;

}

// ================== 2️⃣ 解析数据 ==================

size_t offset = 0;

while (true)

{

// 2.1 不够一个包头

if (buffer.size() - offset < HEAD_LEN)

break;

// 2.2 读取头(不能直接指针长期用)

protocol_head_ head;

memcpy(&head, buffer.data() + offset, HEAD_LEN);

// ⚠️ 如果跨平台,打开这行

// head.DataLen = ntohl(head.DataLen);

int bodyLen = head.DataLen;

// 2.3 长度校验(非常关键)

if (bodyLen <= 0 || bodyLen > MAX_PACKET_SIZE)

{

LOG_DEBUGGING("invalid bodyLen=%d, disconnect", bodyLen);

m_bConnected = false;

break;

}

int totalLen = HEAD_LEN + bodyLen;

// 2.4 不够完整包

if (buffer.size() - offset < totalLen)

break;

// ================== 3️⃣ 处理完整包 ==================

char* packet = buffer.data() + offset;

protocol_head_* pHead = (protocol_head_*)packet;

// 👉 业务分发

if (pHead->MainMsgID == N_NOTIFY && pHead->SubMsgID == N_DACTYLOGRAM__INFO)

{

}

// 处理完一个包 → 偏移

offset += totalLen;

}

// ================== 4️⃣ 清理已处理数据 ==================

if (offset > 0)

{

buffer.erase(buffer.begin(), buffer.begin() + offset);

}

}

#endif

}

相关推荐
clint4561 天前
C++进阶(1)——前景提要
c++
夜悊1 天前
C++代码示例:进制数简单生成工具
c++
郝学胜_神的一滴1 天前
CMake 021: IF 条件判据详诠
c++·cmake
_wyt0012 天前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
LDR0062 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术2 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript
码云数智-园园2 天前
C++20 Modules 模块详解
java·开发语言·spring
swordbob2 天前
NIO的channel中什么是 fd(File Descriptor,文件描述符)
java·开发语言·nio
源分享2 天前
Java线程同步的多种实现方法(非常详细)
java·开发语言·jvm
Luminous.2 天前
C语言--day30
c语言·开发语言