封装一个带大小的封包,防止Tcp粘包拆包

Posted WindSun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了封装一个带大小的封包,防止Tcp粘包拆包相关的知识,希望对你有一定的参考价值。

C++

头文件

#ifndef TCPWRAP_H
#define TCPWRAP_H

#include <memory>
#include <netinet/in.h>
#include <string.h>

#pragma pack(push,1)

typedef struct
{
    size_t length;  //包头(包体长度)
    char* body; //包体
}Packet;

#pragma pack(pop)

class TcpDataWrap
{
private:
    static TcpDataWrap* m_instance;
    TcpDataWrap();

public:
    static TcpDataWrap* instance();

    void sendPackData(int sockfd,char* data,int nLen);

    std::shared_ptr<char> recvUnpackData (int sockfd);
    
};

#endif

源文件

#include "tcpwrap.h"
#include <iostream>
using namespace std;

TcpDataWrap* TcpDataWrap::m_instance = NULL;

TcpDataWrap* TcpDataWrap::instance()
{
    if(m_instance == NULL)
    {
        m_instance = new TcpDataWrap();
    }
    return m_instance;
}

TcpDataWrap::TcpDataWrap() {}


void TcpDataWrap::sendPackData(int sockfd,char* data,int nLen)
{
    Packet p;
    size_t headSize = sizeof(size_t);
    p.length = nLen;
    
    size_t totalLen = headSize + nLen;

    char* buf = new char[totalLen];
    memcpy(buf, &p.length, headSize);
    memcpy(buf + headSize, data, nLen);
    send(sockfd, buf, totalLen,0);
}

//返回一个指向char*的智能指针
std::shared_ptr<char> TcpDataWrap::recvUnpackData (int sockfd)
{
    size_t byteRead=0;
    size_t bodyLen = 0;
    while (byteRead <sizeof(size_t))
    {
        byteRead = recv(sockfd, &bodyLen, sizeof(size_t), MSG_PEEK);    //查看数据长度是否满足头文件的要求
    }
    recv(sockfd, &bodyLen, sizeof(size_t), 0);

    char* data = new char[bodyLen];
    char* pData = data;
    
    size_t byteLeft = bodyLen;

    while (byteLeft>0)
    {
        byteRead = recv(sockfd, data, byteLeft, 0);
        byteLeft -= byteRead;
        pData += byteRead;
    }
    std::shared_ptr<char> ret(data);
    return ret;
}

QT

头文件

#ifndef TCPWRAP_H
#define TCPWRAP_H

#include <memory>
#include <string.h>
#include <QTcpSocket>

#pragma pack(push,1)


typedef struct
{
    size_t length;  //包头(包体长度)
    char* body; //包体
}Packet;

#pragma pack(pop)


class TcpDataWrap : public QObject
{
public:
    static TcpDataWrap* instance();
    void sendPackData(QTcpSocket* sockfd, char* data, int nLen);
    std::shared_ptr<char> recvUnpackData (QTcpSocket* sockfd);

private:
    TcpDataWrap();
    static TcpDataWrap* m_instance;
};

#endif // TCPWRAP_H

源文件

#include "tcpwrap.h"
#include <QTcpSocket>

TcpDataWrap* TcpDataWrap::m_instance = nullptr;

TcpDataWrap* TcpDataWrap::instance()
{
    if(m_instance == nullptr)
    {
        m_instance = new TcpDataWrap();
    }
    return  m_instance;
}

TcpDataWrap::TcpDataWrap(){}

void TcpDataWrap::sendPackData(QTcpSocket* sockfd, char* data, int nLen)
{
    Packet p;
    size_t headSize = sizeof(size_t);
    p.length = nLen;
    size_t totalLen = headSize+nLen;
    char* buf = new char[totalLen];
    memset(buf,0,totalLen);
    memcpy(buf,&p.length,headSize);
    memcpy(buf+headSize,data,nLen);
    sockfd->write(buf,totalLen);
}

std::shared_ptr<char> TcpDataWrap::recvUnpackData (QTcpSocket* sockfd)
{
    size_t byteRead = 0;
    size_t bodyLen;
    while (byteRead<sizeof (size_t))
    {
        byteRead=sockfd->bytesAvailable();
    }

    sockfd->read((char*)&bodyLen,sizeof (size_t));
    char* data = new char[bodyLen];
    char* pData = data;
    size_t byteLeft = bodyLen;
    while (byteLeft>0)
    {
        byteRead=sockfd->read(data,byteLeft);
        byteLeft -= byteRead;
        pData += byteRead;
    }

    std::shared_ptr<char> ret(data);
    return  ret;
}

以上是关于封装一个带大小的封包,防止Tcp粘包拆包的主要内容,如果未能解决你的问题,请参考以下文章

tcp粘包拆包

Netty——解决TCP粘包、拆包

十二.Netty入门到超神系列-TCP粘包拆包处理

12.netty中tcp粘包拆包问题及解决方法

12.netty中tcp粘包拆包问题及解决方法

什么是粘包和拆包,Netty如何解决粘包拆包?