目录
前言
1 应用层
2 在谈协议
3 序列化和反序列化
4 网络版计算器
4.1 指定协议
request结构体
response结构体
4.2 服务端编写
4.3 客户端的编写
5 Json for C++ 的序列化和反序列化使用样例
前言
之前的socket编程,都是在通过系统调用层面,如今我们来向上打通计算机网络。认识应用层的协议和序列化与反序列化
1 应用层
我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层。我们在应用层使用的数据形式一般都是结构体或者对象,而对于网络来说,传输的数据一般都是字节流的
2 在谈协议
为了使数据在网络上能够从源到达目的,网络通信的参与方必须遵循相同的规则,我们将这套规则称为协议(protocol),而协议最终都需要通过计算机语言的方式表示出来。只有通信计算机双方都遵守相同的协议,计算机之间才能互相通信交流。
协议是一种 "约定". socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢?我们通过实现网络版的计算机来深入理解协议的作用。
分析:
- 网络传输的数据也可以是结构体对象,但是最好不要,因为结构体在不同的平台中,有着不同的对齐规则,这样就会导致同一个结构体对象字节流在不同的平台中解析出来的结果不一样;如果想要解析结果需要一样,这样就要求主机和相应的操作系统需要高度的一样,显然,这是不可能的。所以,在网络传输的过程中,需要将结构体序列化成一个大的字符串,为了拿到消息,对于从网络中得到的数据,需要进行反序列化得到相应的信息!
- 协议本质:对方约定好某种格式的数据,常见的就是结构体或者类来进行表达;
- 序列化的作用:方便网络进行通信;
- 反序列化作用:方便上层来设置和获取数据,就是通过. ->操作符来进行读写数据
- 序列化和反序列化并只是单单将结构体的数据变成一串大的字符串,为了方便提取和标识数据,需要加上相关的分割符号和对应的报头。
现在我们通过自我编写协议+使用别人的来深入理解协议!
3 序列化和反序列化
序列化和反序列化:
- 序列化是将对象的状态信息转换为可以存储或传输的形式(字节序列)的过程。
- 反序列化是把字节序列恢复为对象的过程。
OSI七层模型中表示层的作用就是,实现设备固有数据格式和网络标准数据格式的转换。其中设备固有的数据格式指的是数据在应用层上的格式,而网络标准数据格式则指的是序列化之后可以进行网络传输的数据格式。
【序列化和反序列化的目的】
- 在网络传输时,序列化目的是为了方便网络数据的发送和接收,无论是何种类型的数据,经过序列化后都变成了二进制序列,此时底层在进行网络数据传输时看到的统一都是二进制序列。
- 序列化后的二进制序列只有在网络传输时能够被底层识别,上层应用是无法识别序列化后的二进制序列的,因此需要将从网络中获取到的数据进行反序列化,将二进制序列的数据转换成应用层能够识别的数据格式。
我们可以认为网络通信和业务处理处于不同的层级,在进行网络通信时底层看到的都是二进制序列的数据,而在进行业务处理时看得到则是可被上层识别的数据。如果数据需要在业务处理和网络通信之间进行转换,则需要对数据进行对应的序列化或反序列化操作。
4 网络版计算器
此时服务端每次从网络当中获取到的就是一个完整的请求数据,客户端常见的“打包”方式有以下两种。
将结构化的数据组合成一个字符串
约定方案一:
- 客户端发送一个形如“1+1”的字符串。
- 这个字符串中有两个操作数,都是整型。
- 两个数字之间会有一个字符是运算符。
- 数字和运算符之间没有空格。
客户端可以按某种方式将这些结构化的数据组合成一个字符串,然后将这个字符串发送到网络当中,此时服务端每次从网络当中获取到的就是这样一个字符串,然后服务端再以相同的方式对这个字符串进行解析,此时服务端就能够从这个字符串当中提取出这些结构化的数据。
定制结构体+序列化和反序列化
约定方案二:
- 定制结构体来表示需要交互的信息。
- 发送数据时将这个结构体按照一个规则转换成网络标准数据格式,接收数据时再按照相同的规则把接收到的数据转化为结构体。
- 这个过程叫做“序列化”和“反序列化”。
客户端可以定制一个结构体,将需要交互的信息定义到这个结构体当中。客户端发送数据时先对数据进行序列化,服务端接收到数据后再对其进行反序列化,此时服务端就能得到客户端发送过来的结构体,进而从该结构体当中提取出对应的信息。
4.1 指定协议
规定计算式的表达式的有效载荷为: 10 + 20
request结构体
struct Request
{
Request() = default;
Request(int x, int y, char op)
: _x(x), _y(y), _op(op)
{
}
public:
int _x;
int _y;
char _op;
};
response结构体
struct Response
{
Response(int result = 0, int code = 0)
: _result(result), _code(code)
{
}
int _result;
int _code;
};
}
4.2 服务端编写
- socket()打开网络文件
- bind()进行绑定端口和IP地址
- listen()监听
- accept()接受客户,接着创建新的线程给客户提供相应的服务
以上都是socket编程的流程,已经在上一篇博客详细介绍了,便不再介绍,现在我们来重点介绍如何提供服务的!
服务流程:
1. 接受数据
1.1 检查收到的数据是否是完整一条信息
1.2 去掉报头,得到有效载荷
1.3 反序列化
2. 处理任务
2.1 调用上层函数,得到结果的结构体
2.2 将相应的结构体进行序列化
3. 发送数据
3.1 添加报头
3.2 将数据发送到网络
数据读取与发送:添加报头和去掉报头
4.3 客户端的编写
5 Json for C++ 的序列化和反序列化使用样例