-
1、在TCP的连接上,它传输数据的基本形式就是
二进制流
,也就是一段一段的1和0。
-
2、在一般编程语言或者网络框架提供的API中,传输数据的基本形式是
字节
,也就是Byte。一个字节就是8个二进制位,8个Bit。
-
二进制流和字节流本质上是一样的。对于我们编写的程序来说,它需要通过网络传输的数据是结构化的数据,比如,一条命令、一段文本或者一条消息。对应代码中,这些结构化的数据都可以用一个类或者一个结构体来表示。
-
序列化的用途除了用于在网络上传输数据以外,
-
将结构化数据保存在文件中(将对象存储于硬盘上),因为文件内保存数据的形式也是二进制序列。
问题:
在内存里存放的任何数据,它最基础的存储单元也是二进制比特,也就是说,我们应用程序操作的对象,它在内存中也是使用二进制存储的,既然都是二进制,为什么不能直接把内存中,对象对应的二进制数据直接通过网络发送出去,或者保存在文件中呢?为什么还需要序列化和反序列化呢?
-
内存里存的东西,不通用, 不同系统, 不同语言的组织可能都是不一样的, 而且还存在很多引用,指针,并不是直接数据块。内存中的对象数据应该具有语言独特性,例如表达相同业务的User对象(id/name/age字段),Java和PHP在内存中的数据格式应该不一样的,如果直接用内存中的数据,可能会造成语言不通。只要对序列化的数据格式进行了协商,任何2个语言直接都可以进行序列化传输、接收。
-
一个数据结构,里面存储的数据是经过非常多其他数据通过非常复杂的算法生成的,因为数据量非常大,因此生成该数据结构所用数据的时间可能要非常久,生成该数据结构后又要用作其他的计算,那么你在调试阶段,每次执行个程序,就光生成数据结构就要花上这么长的时间。假设你确定生成数据结构的算法不会变或不常变,那么就能够通过序列化技术生成数据结构数据存储到磁盘上,下次又一次执行程序时仅仅须要从磁盘上读取该对象数据就可以,所花费时间也就读一个文件的时间。
-
虽然都是二进制的数据,但是序列化的二进制数据是通过一定的协议将数据字段进行拼接。第一个优势是:不同的语言都可以遵循这种协议进行解析,实现了跨语言。第二个优势是:这种数据可以直接持久化到磁盘,从磁盘读取后也可以通过这个协议解析出来。
要想使用网络框架的API来传输结构化的数据,必须得先实现结构化的数据与字节流之间的双向转换。这种将结构化数据转换成字节流的过程,称为序列化,反过来转换,就是反序列化。
-
简单来说,序列化就是将对象实例的状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它依据流重构对象。这两个过程结合起来,能够轻松地存储和数据传输。
-
比如,能够序列化一个对象,然后使用HTTP 通过 Internet 在client和server之间传输该对象。
-
1、可读性
-
2、实现复杂度
-
3、性能
-
4、信息密度
-
序列化后的信息密度越大越好,也就是说,同样的一个结构化数据,序列化之后占用的存储空间越小越好
03 | 08 7a 68 61 6e 67 73 61 6e | 17 | 01
User | z h a n g s a n | 23 | true
- 1.首先我们需要标识一下这个对象的类型,这里面我们用一个字节来表示类型,比如用 03 表示这是一个 User 类型的对象。
- 2.我们约定,按照 name、age、married 这个固定顺序来序列化这三个属性。按照顺序,第一个字段是 name,我们不存字段名,直接存字段值“zhangsan”就可以了,由于名字的长度不固定,我们用第一个字节 08 表示这个名字的长度是 8
个字节,后面的 8 个字节就是 zhangsan。 - 3.第二个字段是年龄,我们直接用一个字节表示就可以了,23 的 16 进制是 17 。
- 4.最后一个字段是婚姻状态,我们用一个字节来表示,01 表示已婚,00 表示未婚,这里面保存一个 01。
同样的一个User对象,JSON序列化后({"name":"zhangsan","age":"23","married":"true"})
- JSON序列化后需要47个字节,专用的序列化方法只要12个字节就够了。
- 专用的序列化方法显然更高效,序列化出来的字节更少,在网络传输过程中的速度也更快。但缺点是,需要为每种对象类型定义专门的序列化和反序列化方法,实现起来太复杂了,大部分情况下是不划算的。
#include <iostream>
#include <fcntl.h>
#include <vector>
#include <stdio.h>
using namespace std;
class CA
private:
int x;
CA()
x = ;
CA(int y):x(y)
virtual ~CA()
public:
int Serialize(const char* pFilePath) const
int isSrlzed = -;
FILE* fp;
if ((fp = fopen(pFilePath, "w+")) == NULL)
printf("file opened failure\n");
return -;
isSrlzed = fwrite(&x, sizeof(int), , fp);
if ((- == isSrlzed) || ( == isSrlzed))
printf("Serialize failure\n");
return -;
if(fclose(fp) != )
printf("Serialize file closed failure.\n");
return -;
printf("Serialize succeed.\n");
return ;
}
int Deserialize(const char* pFilePath)
int isDsrlzed = -;
FILE* fp;
if ((fp = fopen(pFilePath, "r+")) == NULL)
printf("file opened failure.\n");
return -;
isDsrlzed = fread(&x, sizeof(int), , fp);
if ((- == isDsrlzed)||( == isDsrlzed))
printf("Deserialize failure.\n");
return -;
if(fclose(fp) != )
printf("Deserialize file closed failure.\n");
return -;
printf("Deserialize succeed.\n");
return ;
}
void Show()
cout<< "in Show():"<< x << endl;
int main(int argc, char const *argv[])
CA as();
as.Serialize("data.txt");
CA ad;
ad.Deserialize("data.txt");
ad.Show();
return ;
更详细的案例,简单注释:
1、CharVec.h : 定义一个 vector < char> 类型字节数组,也就是一个定义的容器,为下面的DataStream中存放数据提供接口:
#ifndef CHARVEC_H
#define CHARVEC_H
#include <memory>
class CharVec{
public:
CharVec();
CharVec(const CharVec &vec);
CharVec &operator =(const CharVec &vec);
~CharVec();
bool operator ==(const CharVec &vec) const;
size_t size() const;
size_t capacity() const;
char *begin() const;
char *end() const;
void push(const char *data, int len);
void push(const std::string &str);
void push(char c);
void removeFromFront(int len);
void clear();
private:
void checkAndAlloc();
void reallocate();
void free();
std::pair<char *, char *> allocAndCopy(char *begin, char *end);
private:
char *m_Elements;
char *m_FirstFree;
char *m_Cap;
std::allocator<char> m_Allocator;
#endif
2、CharVec.cpp :对CharVec.h 声明的函数进行定义
#include "CharVec.h"
CharVec::CharVec() :m_Elements(nullptr), m_FirstFree(nullptr),m_Cap(nullptr)
{}
CharVec::CharVec(const CharVec &vec)
auto newData = allocAndCopy(vec.begin(), vec.end());
m_Elements = newData.first;
m_FirstFree = newData.second;
m_Cap = newData.second;
CharVec &CharVec::operator =(const CharVec &vec)
auto newData = allocAndCopy(vec.begin(), vec.end());
free();
m_Elements = newData.first;
m_FirstFree = newData.second;
m_Cap = newData.second;
return *this;
CharVec::~CharVec()
free();
bool CharVec::operator ==(const CharVec &vec) const
if (m_Elements == vec.m_Elements &&
m_FirstFree == vec.m_FirstFree &&
m_Cap == vec.m_Cap) {
return true;
return false;
size_t CharVec::size() const
return m_FirstFree - m_Elements;
size_t CharVec::capacity() const
return m_Cap - m_Elements;
char *CharVec::begin() const
return m_Elements;
char *CharVec::end() const
return m_FirstFree;
void CharVec::push(const char *data, int len)
if (len <= 0) {
return ;
for (int i = 0; i < len; ++i) {
push(data[i]);
void CharVec::push(const std::string &str)
push(str.c_str(), str.size());
void CharVec::push(char c)
checkAndAlloc();
m_Allocator.construct(m_FirstFree++, c);
void CharVec::removeFromFront(int len)
if (len > size()) {
return ;
char *from = m_Elements;
char *to = m_Elements + len;
m_Elements += len;
for (int i = 0; i < len; ++i) {
m_Allocator.destroy(--to);
m_Allocator.deallocate(from, m_Elements - from);
void CharVec::clear()
free();
m_Elements = nullptr;
m_FirstFree = nullptr;
m_Cap = nullptr;
void CharVec::checkAndAlloc()
if (size() == capacity()) {
reallocate();
void CharVec::reallocate()
auto newCapacity = size() ? 2 * size() : 1;
auto newData = m_Allocator.allocate(newCapacity);
auto dest = newData;
auto ele = m_Elements;
for (size_t i = 0; i != size(); ++i) {
m_Allocator.construct(dest++, std::move(*ele++));
free();
m_Elements = newData;
m_FirstFree = dest;
m_Cap = m_Elements + newCapacity;
void CharVec::free()
if (m_Elements) {
for (auto p = m_FirstFree; p != m_Elements;) {
m_Allocator.destroy(--p);
m_Allocator.deallocate(m_Elements, m_Cap - m_Elements);
std::pair<char *, char *> CharVec::allocAndCopy(char *begin, char *end)
auto startPos = m_Allocator.allocate(end - begin);
return {startPos, std::uninitialized_copy(begin, end, startPos)};
3、DataHeader类的声明:定义id,及headerlen,totalLen相关客户属性
#ifndef DATAHEADER_H
#define DATAHEADER_H
struct DataHeader
DataHeader(int id = 0);
bool operator==(const DataHeader &header);
void reset();
const static int s_HeaderLen = 3 * sizeof(int);
int m_Id;
int m_HeaderLen;
int m_TotalLen;
#endif
3、DataStream.h:
- 支持序列化和反序列化操作,
- 枚举继承char类型意思是说这个枚举里的枚举值底层是用char来存储的
- 支持是序列化相关的数据类型,
- 往这个类的写数据和读数据都是提供了两种形式,
- 流式操作符(<< 或者 >> )和函数(readVal,writeVal)
#ifndef DATASTREAM_H
#define DATASTREAM_H
#include <memory>
#include <map>
#include <list>
#include <vector>
#include <set>
#include "DataHeader.h"
#include "CharVec.h"
class CustomTypeInterface;
class DataStream
public:
DataStream(std::unique_ptr<DataHeader> *header = nullptr);
DataStream(const DataStream &stream);
DataStream& operator =(const DataStream &stream);
enum class DataType : char {
UnKnown,
Boolean,
Char,
WChar,
Int,
UInt,
Int64,
Double,
String,
WString,
Vector,
List,
Map,
Set,
CustomType,
bool operator == (const DataStream &stream) const;
int totalSize() const { return m_Header->m_TotalLen; }
int headerSize() const { return m_Header->m_HeaderLen; }
int dataSize() const {return m_Header->m_TotalLen - m_Header->m_HeaderLen;}
void clear();
void writeHeader();
void writeData(const char *data, int len);
DataStream& operator<<(char val);
void writeVal(char val);
DataStream& operator<<(wchar_t val);
void writeVal(wchar_t val);
DataStream& operator <<(bool val);
void writeVal(bool val);
DataStream& operator <<(int val);
void writeVal(int val);
DataStream& operator <<(unsigned int val);
void writeVal(unsigned int val);
DataStream& operator <<(int64_t val);
void writeVal(int64_t val);
DataStream& operator <<(double val);
void writeVal(double val);
DataStream& operator <<(const std::string &val);
void writeVal(const std::string &val);
DataStream& operator <<(const std::wstring &val);
void writeVal(const std::wstring &val);
DataStream& operator <<(CustomTypeInterface *val);
void writeVal(CustomTypeInterface *val);
template<typename T>
DataStream& operator <<(const std::vector<T>& val);
template<typename T>
void writeVal(const std::vector<T>& val);
template<typename T>
DataStream& operator <<(const std::list<T>& val);
template<typename T>
void writeVal(const std::list<T>& val);
template<typename T1, typename T2>
DataStream& operator <<(const std::map<T1, T2>& val);
template<typename T1, typename T2>
void writeVal(const std::map<T1, T2>& val);
template<typename T>
DataStream& operator <<(const std::set<T>& val);
template<typename T>
void writeVal(const std::set<T>& val);
void readHeader(const char *data);
template<typename T>
bool readData(T *val);
bool operator>>(char &val);
bool readVal(char &val);
bool operator>>(wchar_t& val);
bool readVal(wchar_t &val);
bool operator>>(bool &val);
bool readVal(bool &val);
bool operator>>(int &val);
bool readVal(int &val);
bool operator>>(unsigned int &val);
bool readVal(unsigned int &val);
bool operator>>(int64_t &val);
bool readVal(int64_t &val);
bool operator>>(double &val);
bool readVal(double &val);
bool operator>>(std::string &val);
bool readVal(std::string &val);
bool operator>>(std::wstring &val);
bool readVal(std::wstring &val);
bool operator>>(CustomTypeInterface *val);
bool readVal(CustomTypeInterface *val);
template<typename T>
bool operator>>(std::vector<T> &val);
template<typename T>
bool readVal(std::vector<T> &val);
template<typename T>
bool operator>>(std::list<T> &val);
template<typename T>
bool readVal(std::list<T> &val);
template<typename T1, typename T2>
bool operator>>(std::map<T1, T2> &val);
template<typename T1, typename T2>
bool readVal(std::map<T1, T2> &val);
template<typename T>
bool operator>>(std::set<T> &val);
template<typename T>
bool readVal(std::set<T> &val);
int Serialize(char *buf) const;
bool Deserialize(const char *buf, int len);
private:
std::unique_ptr<DataHeader> m_Header;
CharVec m_DataBuffer;
int m_IsFirstWrite;
4、DataStream.cpp:DataStream.h文件相关函数的实现
#include "DataStream.h"
#include "CustomTypeInterface.h"
DataStream::DataStream(std::unique_ptr<DataHeader> *header) :
m_IsFirstWrite(true)
if (header == nullptr) {
m_Header.reset(new DataHeader);
else {
m_Header.reset(header->release());
DataStream::DataStream(const DataStream &stream)
operator =(stream);
DataStream &DataStream::operator =(const DataStream &stream)
if (&stream == this) {
return *this;
m_Header.reset(new DataHeader);
*m_Header = *stream.m_Header;
m_DataBuffer = stream.m_DataBuffer;
m_IsFirstWrite = stream.m_IsFirstWrite;
return *this;
bool DataStream::operator ==(const DataStream &stream) const
if (&stream == this) {
return true;
if (m_Header.get() == stream.m_Header.get() &&
m_DataBuffer == stream.m_DataBuffer) {
return true;
return false;
void DataStream::clear()
m_IsFirstWrite = true;
m_DataBuffer.clear();
m_Header->reset();
void DataStream::writeHeader()
int headerLen = DataHeader::s_HeaderLen;
writeData((char *)&(m_Header->m_TotalLen), sizeof(int));
writeData((char *)&headerLen, sizeof(int));
writeData((char *)&m_Header->m_Id, sizeof(int));
m_Header->m_HeaderLen = headerLen;
void DataStream::writeData(const char *data, int len)
if (len == 0) {
return ;
if (m_IsFirstWrite) {
m_IsFirstWrite = false;
writeHeader();
m_DataBuffer.push(data, len);
m_Header->m_TotalLen += len;
memcpy(m_DataBuffer.begin(), &m_Header->m_TotalLen, sizeof(int));
void DataStream::writeVal(char val)
char type = (char)DataType::Char;
writeData((char *)&(type), sizeof(char));
writeData(&val, sizeof(char));
void DataStream::writeVal(const std::string &val)
char type = (char)DataType::String;
writeData((char *)&(type), sizeof(char));
int size = val.size();
writeVal(size);
writeData(val.c_str(), size);
void DataStream::writeVal(CustomTypeInterface *val)
val->serialize(*this, (char)DataType::CustomType);
void DataStream::readHeader(const char *data)
int *p = (int *)data;
m_Header->m_TotalLen = *p++;
m_Header->m_HeaderLen = *p++;
m_Header->m_Id = *p++;
m_Header->m_TotalLen -= m_Header->m_HeaderLen;
m_Header->m_HeaderLen = 0;