编写一个基于TCP协议的包解析器

总体思路:
这里用select IO模型
当接收到网络数据流的时候, 直接把数据丢到一个缓冲区中去
这里封装了对缓冲区操作的类, 提供的接口操作包括:
1.添加(追加)流数据到缓冲区
2.取出缓冲区第一个合法的包(拆掉包头包尾等数据)

就两个操作接口, 很简单的操作

为了方便操作数据流, 可以用一些现成的容器去处理, 如果用Qt开发, 可以用QByteArray, 如果用VC开发, 可以用string

Qt:
.h

class DataPack : public QObject

{

    Q_OBJECT

public:

    explicit DataPack(QObject *parent = 0);

    

    bool appendStream(char* data, int len);

    QByteArray Pack();



private:

    QByteArray buf;

    QMutex muxBuf;

};


.cpp

DataPack::DataPack(QObject *parent) :

    QObject(parent)

{

    buf.clear();

}



bool DataPack::appendStream(char *data, int len)

{

    QMutexLocker locker(&muxBuf);

    if(buf.size() > 64 * 1024)

    {//缓存超过64k

        return false;

    }

    buf.append(data, len);

    return true;

}



QByteArray DataPack::Pack()

{//检测缓冲区, 取出第一个合法数据包

    QByteArray front = "KT";    //包头标志

    QByteArray tail = "END";    //包尾标志



    QMutexLocker locker(&muxBuf);

    do

    {

        if(-1 == buf.indexOf(front))

        {//未有KT开头的标志位, 数据流非法, 清空缓存

            buf.clear();

            return 0;

        }

        if(!buf.startsWith(front))

        {//缓存区数据不为KT标志开头, 肯能存在垃圾数据, 删除前面部分数据流到已KT开始为止

            buf.remove(0, buf.indexOf(front));

        }

        //"KT" len flag data(不定大小) crc "END"

        if(buf.length() < 17)

            return 0;

        qint32 len = *reinterpret_cast<qint32*>(buf.mid(2, 4).data());



        if(len <= 0 || len > 32 * 1024)

        {//包的大小有误, 非法数据 或 之前找的 KT开头标志有误, 继续找下一个包头标志

            buf.remove(0, 2); //删除KT

            continue;

        }

        if(buf.size() < len + 6)

        {//非完整包

            return 0;

        }

        quint32 flag = *reinterpret_cast<quint32*>(buf.mid(2 + 4, 4).data());

        quint32 crc = *reinterpret_cast<quint32*>(buf.mid(2 + 4 + len - 7, 4).data());

        if(tail != buf.mid(2 + 4 + len - 3, 3))

        {//包尾标志错误, 丢弃这个包缓存

            buf.remove(0, 2 + 4 + len);

            continue;

        }

        QByteArray pack = buf.mid(2 + 4 + 4, len - 4 - 3);

        buf.remove(0, 2 + 4 + len);

        return pack;

    }while(true);

}


这里的解析是针对一个特定的结构来处理的, 这里处理的包结构是这样的(内存分布):

"KT"    //包头标志 --16位
len    //后面数据长度 --32位
flag    //包的标志(什么标志可以自己定, 看具体业务需要) --32位
data    //包数据(可以进一步封装成一定结构) --不定长
crc    //数据包校验值(校验用, 看具体业务需不需要用) --32位
"END"    //包尾标志 --24位

整合成一个结构体:

typedef struct _Pack

{

    char front[2];

    long len;

    long flag;

    struct Data{

        ...

    };

    long crc;

    char end[3];

    _Pack() {

        front[0] = 'K';

        front[1] = 'T';

        ...

        end[0] = 'E';

        end[1] = 'N';

        end[2] = 'D';

    }

}Pack;


注意要内存对齐, 要这样做:

#pragma pack(1)

typedef struct _Pack

{

    char front[2];

    long len;

    long flag;

    struct Data{

        ...

    };

    long crc;

    char end[3];

    _Pack() {

        front[0] = 'K';

        front[1] = 'T';

        ...

        end[0] = 'E';

        end[1] = 'N';

        end[2] = 'D';

    }

}Pack;

#pragma pack()

 

VC:
.h

class XXX

{

public:

    string Pack();        //返回数据包

private:

    string recvBuf;        //接收缓存区

};


.cpp

string XXX::Pack()

{

    string pack;

    if(this->recvBuf.size() <= 0)

        return "";

    const string front = "KT";        //包开头标志

    const string end = "END";        //包结尾标志



    do

    {

        int pos = this->recvBuf.find_first_of(front);

        if(string::npos == pos)

        {//数据流缓存区未找到开头标志的包, 清空缓存区

            this->recvBuf = "";

            return "";

        }

        if(0 != pos)

        {//缓冲区开头数据不为"KT", 删除掉KT标志前的"垃圾"数据

            this->recvBuf.erase(0, pos);

        }

        // "KT" len flag data crc "END"

        if(this->recvBuf.size() < 17)

            return "";

        int len = *reinterpret_cast<const int*>(this->recvBuf.c_str() + 2);

        if(len <= 0 || len > 32 * 1024)

        {//包的大小有误, 非法数据 或 之前找的 KT开头标志有误, 继续找下一个包头标志

            this->recvBuf.erase(0, 2);

            continue;

        }

        if(this->recvBuf.size() < len + 6)

        {//不完整包

            return "";

        }

        int flag = *reinterpret_cast<const int*>(this->recvBuf.c_str() + 2 + 4);

        int crc = *reinterpret_cast<const int*>(this->recvBuf.c_str() + 2 + 4 + len - 3 - 4);

        if(0 != this->recvBuf.substr(2 + 4 + len - 3, 3).compare(end))

        {//不是已END结果的包, 丢弃这个包

            this->recvBuf.erase(0, 2 + 4 + len);

            continue;

        }

        

        pack = this->recvBuf.substr(2 + 4 + 4, len - 4 - 3);

        this->recvBuf.erase(0, 2 + 4 + len);

        return pack;

    }while (true);

    return pack;

}




添加到缓冲区就直接写this->recvBuf.append(buf, bufLen);

 



ok了, 就是这样而已, 代码是随便写出来的demo, 没做严格的测试, 没经过推敲优化...只是写下一个很朴素的思路...


!!!!!!!!!!!!!!!!!!!!!!!!!!!....................2222222222


你可能感兴趣的:(tcp)