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 tmp[4096];

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

}

相关推荐
qingyulee2 小时前
python redis
开发语言·redis·python
努力努力再努力wz2 小时前
【MySQL 进阶系列】拒绝滥用root:从 mysql.user 到权限校验,带你彻底理解用户管理与授权机制!
android·c语言·开发语言·数据结构·数据库·c++·mysql
超梦dasgg3 小时前
智慧充电系统订单服务Java 实现方案
java·开发语言·微服务
雪度娃娃3 小时前
基于TCP的网络词典
网络·c++·tcp/ip·c#
装杯让你飞起来啊3 小时前
Kotlin List / Array 与 for 循环
开发语言·kotlin·list
南滑散修3 小时前
红黑树-非黑即红
java·开发语言
2501_932750263 小时前
Java IO流基础全面详解:字节流、字符流
java·开发语言
冰暮流星3 小时前
javascript之默认事件
开发语言·javascript·ecmascript