近日,在项目里,需要对tcp传输的数据进行序列化和反序列化,有很多方法,记录下来
写在前面:使用tcp传输的时候需要注意字节对齐的问题,在以下代码中统一使用单字节对齐
cpp
//单字节对齐 写在结构体定义之上
#pragma pack(1)
第一种 QT
如果是用QT写的,结构体拿到值以后,可以使用QDataStream
来进行序列化和反序列化,可以参考这个链接
cpp
struct MsgBody
{
int iValue; // 下面两个vector的size
double 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 序列化和反序列化不能重载 string
QString 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
就是把二进制转为结构体,使用方法
cpp
MsgBody body;
// 对 body 的各个变量赋值
QByteArray packet;
body.write(&packet);
body.read(packet); // packet为二进制
这种方法就是在使用QT写tcp的时候好用
注:
QT如果是string
类型中包含有中文的话,需要转编码格式,上述的编码格式转换适用于windows
第二种 强转
强转的方法适用于结构体中没有 可变长度的变量,可变长度就是结构体中包含string, vector
类型
cpp
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的话更简单了
cpp
// 还是上面的结构体,在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
,参考这个
cpp
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
类型中有中文的也不用担心编码格式了