序列化与反序列化

1. 引言

进行网络通信时,很重要的一点,就是要把想传输给其它主机的数据,进行序列化之后,才能调用如write这样的接口,发送到网络中。

对方接收到后,解析所接收到的数据,还原为原始数据,这就是反序列化的过程。

2. 理解

如何理解序列化呢?

主机A与主机B要进行网络通信,主机A有一份数据,想通过网络,发送给B,首先,A要把原始数据,转换成一种更通用,且便于解析的格式的数据。就是要把原始数据转换为另一种格式的数据,这种格式的数据能够很方便地被其它主机,正确地解析为A最初想要发送的数据。这就是序列化。

2.1 序列化的必要性

网络通信前,为什么要序列化?

因为一台主机上的数据,它不能直接以它在当前主机中,在内存存储的格式,去直接传输给其它主机,原因如结构体的对齐规则不同、指针数据在对方主机中无法使用等。

这就使得其它主机如果接收到的是直接传递的原始数据时,无法正确地去把它解析到当前主机,所以序列化的作用就是,把我当前主机想要传输的数据,转换为一种更通用、方便解析的格式的数据,这个转换的过程就是序列化。

其它主机接收到这个转换后的数据,可以进行正确地解析,从而把其它主机真正想要传输的数据,去存放在当前主机的内存、磁盘中。

2.1.1 结构体的内存对齐规则不同

C/C++ 编译器为了提高内存访问效率,会给结构体加填充字节(padding),但填充规则不是统一的:

一、不同编译器(GCC vs MSVC)对齐规则不同;计算结构体的大小时,首先要计算出,每个成员的对齐数 。对齐数 = min(该成员的大小,编译器默认的对齐数)

比如VS下的默认对齐数是8

二、默认对齐数是可以修改的,#pragma pack(4) ,可以把默认对齐数修改为4

代码:

综上,如果不对结构体对象进行处理,而选择直接发送的话,

比如直接使用send接口向网络中发送数据:

cpp 复制代码
struct Student
{
    int    id;       // 4字节
    char   name[7];  // 7字节
    double  score;   // 8字节
};

Student stu = {1001, "zhangs", 95.5};
send(fd, &stu, sizeof(stu), 0);  

主机A的默认对齐数为4,此时sizeof(stu)为20;

主机B的默认对齐数为8,此时sizeof(stu)为24。

A调用recv,接收到B发来的数据,只会对前20个字节进行处理,解析出的数据,一定是错误的!

2.1.2 大、小端字节序问题

小端机:存储数值型数据时,低权值的字节,存放在低地址处。

如图:

大端机:存储数值型数据时,低权值的字节,存放在高地址处。

一些主机是大端机,一些主机是小端机。所以在内存中存储变量时,存储的格式是不同的。

网络通信为了解决这个问题,规定了网络字节序,采用大端字节序。所以向网络中发送数据前,必须要把保证数据转换为大端字节序。

如果两台主机分别是大端机和小端机,此时不加转换地直接发送原始数据,对方解析后,就是完全错误的!

2.1.3 数据类型长度不一致

而且在16位机器上,int类型 通常占用2个字节

2.1.4 传递指针数据

指针是本地内存的地址编号 ,这个编号只在当前主机的内存空间有效,对方主机的内存地址完全独立,所以传输指针地址毫无意义。

A主机传递一个指针数据,这个指针指向的是A主机中的一段合法的、申请成功的内存,但是如果直接传递给B主机,对于B主机来说,这个指针指向的内存数据,

  • 要么是无效地址(访问崩溃);
  • 要么是主机B自己的内存数据,解析出一堆乱码,完全不是A主机原本想传输的数据。

而序列化要做的,就是「抛弃指针地址,传输指针指向的真实数据内容」。

2.1.5 无法适配TCP 面向字节流的特性

TCP 是 "无边界的字节流",直接 send 结构体会遇到两个问题:

一、粘包:如果连续 send 两个 Student 结构体,服务端无法区分 "第一个结构体的末尾" 和 "第二个结构体的开头";

二、拆包:如果结构体数据被 TCP 拆成两段发送,服务端只收到一半数据,直接解析会导致内存越界、程序崩溃。

而序列化时,我们可以给字节流加长度前缀 (比如[总长度(4字节)][id(4)][name(8)][score(4)]),服务端先读长度,再读对应字节数,完美解决粘包 / 拆包 ------ 直接 send 结构体完全做不到这一点。

相关推荐
小成202303202651 天前
Linux高级02
linux·开发语言
mounter6251 天前
【硬核前沿】CXL 深度解析:重塑数据中心架构的“高速公路”,Linux 内核如何应对挑战?-- CXL 协议详解与 LSF/MM 最新动态
linux·服务器·网络·架构·kernel
++==1 天前
Linux 进程间通信与线程同步技术详解:IPC 机制、线程 API、同步工具与经典同步问题
linux
特长腿特长1 天前
centos、ubantu系列机的用户和用户组的结构是什么?具体怎么配置?用户组权限怎么使用?这篇文章持续更新,帮助你复习linux的基础知识
linux·运维·centos
zzzyyy5381 天前
Linux环境变量
linux·运维·服务器
pluvium271 天前
记对 xonsh shell 的使用, 脚本编写, 迁移及调优
linux·python·shell·xonsh
无级程序员1 天前
centos7 安装 llvm-toolset-7-clang出错的问题解决
linux·centos
CHHC18801 天前
NetCore树莓派桌面应用程序
linux·运维·服务器
ACP广源盛139246256731 天前
破局 Type‑C 切换器痛点@ACP#GSV6155+LH3828/GSV2221+LH3828 黄金方案
c语言·开发语言·网络·人工智能·嵌入式硬件·计算机外设·电脑
云栖梦泽1 天前
Linux内核与驱动:9.Linux 驱动 API 封装
linux·c++