近日,在项目里,需要对tcp传输的数据进行序列化和反序列化,有很多方法,记录下来
写在前面:使用tcp传输的时候需要注意字节对齐的问题,在以下代码中统一使用单字节对齐
//单字节对齐 写在结构体定义之上
#pragma pack(1)
第一种 QT
如果是用QT写的,结构体拿到值以后,可以使用QDataStream
来进行序列化和反序列化,可以参考这个链接
struct MsgBody
{int iValue; // 下面两个vector的sizedouble dValue;string strValue;vector<int> vStrSize; vector<string> vStr; //写数据到类数据成员中void write(QByteArray *data){QDataStream streamWriter(data, QIODevice::WriteOnly);streamWriter.setVersion(QDataStream::Qt_5_14);streamWriter << iValue;streamWriter << dValue;// string 需要转 QString 才能使用 << ,同时如果 string 中有中文,还需要注意当前的编码格式转换// 使用 QDataStream 序列化和反序列化不能重载 stringQString qstr = QString::fromLocal8Bit(strValue.c_str());streamWriter << qstr.toLocal8Bit();for (int i = 0; i < iValue; i++){int iTemp = vStrSize[i];streamWriter << iTemp;}for (int j = 0; j < iValue; j++){string strValue = vStr[j];QString qstrValue = QString::fromLocal8Bit(strValue.c_str());streamWriter << qstrValue.toLocal8Bit();}}//读数据到类数据成员中void read(QByteArray &data){QDataStream streamReader(&data, QIODevice::ReadOnly);streamReader.setVersion(QDataStream::Qt_4_3);streamReader >> iValue;streamReader >> dValue;QByteArray byValue;streamReader >> byValue;QString qstr = QString::fromLocal8Bit(byValue);strValue = string((const char *)qstr.toLocal8Bit());for (int i = 0; i < iValue; i++){int iTemp;streamReader >> iTemp;vStrSize.push_back(iTemp);}for (int j = 0; j < iValue; j++){QByteArray qstrValue;streamReader >> qstrValue;QString str = QString::fromLocal8Bit(qstrValue);string strValue = string((const char *)str.toLocal8Bit());vStr.push_back(strValue);}}
};
在代码中write
就是把结构体转为二进制,read
就是把二进制转为结构体,使用方法
MsgBody body;
// 对 body 的各个变量赋值
QByteArray packet;
body.write(&packet);body.read(packet); // packet为二进制
这种方法就是在使用QT写tcp的时候好用
注:
QT如果是string
类型中包含有中文的话,需要转编码格式,上述的编码格式转换适用于windows
第二种 强转
强转的方法适用于结构体中没有可变长度的变量,可变长度就是结构体中包含string, vector
类型
struct NET_HEAD
{
public:int recvId; // 接收消息方 int sendId; // 发送消息方 int msgType; // 消息类型int dataLen; // 数据长度
};int main()
{char *m_Msg_buf = new char[40960];;//消息缓冲区char *m_Recv_buf= new char[4096];;//消息缓冲区NET_HEAD head;// 结构体转为二进制memcpy((void *)m_Msg_buf, (void *)&head, sizeof(NET_HEAD ));// s_server 是tcp连接int recv_len = recv(s_server, m_Recv_buf, 4096, 0);memcpy(m_Msg_buf, m_Recv_buf, recv_len);// 二进制强转为结构体 NET_HEAD* header = (NET_HEAD*)m_Msg_buf;
}
如果使用QT的话更简单了
// 还是上面的结构体,在main函数中写以下代码
NET_HEAD head;
// 为 head 赋值
QByteArray packet;
packet.append((char*)&head, sizeof(NET_HEAD)); // 将 结构体强转为二进制// 二进制转为结构体
// packet 是接受到的二进制
NET_HEAD * head1 = (NET_HEAD *)packet.data();
第三种 纯c++
用纯C++来进行序列化和反序列化可变结构体比较有意思,其实也就是memcpy
,参考这个
struct ComplexData {int id;int len;string name;int iStrVecLen;vector<int> viStr;vector<string> vStr;int valuesLen;vector<double> values;char* serialize() const {int offset = 0;char* buffer = new char[sizeof(ComplexData)];memcpy(buffer + offset, &this->id, sizeof(int));offset += sizeof(int);memcpy(buffer + offset, &this->len, sizeof(int));offset += sizeof(int);memcpy(buffer + offset, (this->name).data(), this->len);offset += this->len;offset += 1; // 加1 是为了防止string类型后面有 \0 memcpy(buffer + offset, &this->iStrVecLen, sizeof(int));offset += sizeof(int);for (int i = 0; i < this->iStrVecLen; i++){int iValue = this->viStr[i];memcpy(buffer + offset, &iValue, sizeof(int));offset += sizeof(int);}for (int i = 0; i < this->iStrVecLen; i++){string str = this->vStr[i];int sizeStr = this->viStr[i];memcpy(buffer + offset, str.data(), sizeStr);offset += sizeStr;offset += 1;}memcpy(buffer + offset, &this->valuesLen, sizeof(int));offset += sizeof(int);for (int i = 0; i < this->valuesLen; i++){memcpy(buffer + offset, &(this->values[i]), sizeof(double));offset += sizeof(double);}return buffer;}// 反序列化函数void deserialize(char* buffer) {int offset = 0;memcpy(&this->id, buffer + offset, sizeof(int));offset += sizeof(int);memcpy(&this->len, buffer + offset, sizeof(int));offset += sizeof(int);this->name = std::string(buffer + offset, this->len);offset += this->len;offset += 1;memcpy(&this->iStrVecLen, buffer + offset, sizeof(int));offset += sizeof(int);for (int i = 0; i < this->iStrVecLen; i++){int iValue;memcpy(&iValue, buffer + offset, sizeof(int));offset += sizeof(int);this->viStr.push_back(iValue);}for (int i = 0; i < this->iStrVecLen; i++){int iValue = this->viStr[i];string str = std::string(buffer + offset, iValue);offset += iValue;offset += 1;this->vStr.push_back(str);}memcpy(&this->valuesLen, buffer + offset, sizeof(int));offset += sizeof(int);for (int i = 0; i < this->valuesLen; i++){double dValue;memcpy(&dValue, buffer + offset, sizeof(double));offset += sizeof(double);this->values.push_back(dValue);}}
};int main()
{string name = "Join Doe";vector<double> vec;vec.push_back(1.1);vec.push_back(2.2);vec.push_back(3.3);string str1 = "teards sfdf";string str2 = "sdsHJJMHj";string str3 = "测试代码";string str4 = "123qweer长度";vector<string> vecStr;vecStr.push_back(str1);vecStr.push_back(str2);vecStr.push_back(str3);vecStr.push_back(str4);vector<int> vecIStr;vecIStr.push_back(str1.size());vecIStr.push_back(str2.size());vecIStr.push_back(str3.size());vecIStr.push_back(str4.size());ComplexData oriData;oriData.id = 1;oriData.len = name.size();oriData.name = name;oriData.iStrVecLen = vecStr.size();oriData.viStr = vecIStr;oriData.vStr = vecStr;oriData.valuesLen = vec.size();oriData.values = vec;// 序列化并发送char* buffer = oriData.serialize();// 接收并反序列化ComplexData receivedObj;receivedObj.deserialize(buffer);cout << "recvData.id: " << receivedObj.id << endl;cout << " recvData.name: " << receivedObj.name << endl;for (int i = 0; i < receivedObj.values.size(); i++){cout << receivedObj.values[i] << endl;}for (int i = 0; i < receivedObj.vStr.size(); i++){cout << receivedObj.vStr[i] << endl;}return 0;
}
纯C++序列化和反序列化对于string
类型中有中文的也不用担心编码格式了