1.应用层
1.1.再谈应用层
程序员编写的网络程序,旨在解决实际问题并满足日常需求,均运行在应用层。
1.2.再谈'协议'
根据前面的知识,我们知道协议是一种'约定'。
Socket API 的接口在读写数据时,默认按'字符串'方式发送接收。如果我们要传输一些'结构化的数据'怎么办呢?
协议的真正概念:其实,协议就是双方约定好的结构化数据!
2.初识序列化与反序列化

2.1.基本概念
- 序列化:把信息由多变一,方便网络发送。
- 反序列化:把信息由一变多,方便上层处理。
举个例子:例如我们需要实现一个服务器版的加法器。客户端需要把要计算的两个加数发过去,由服务器进行计算,最后再把结果返回给客户端。
约定方案:
- 定义结构体来表示我们需要交互的信息;
- 发送数据时将这个结构体按照一个规则转换成字符串,接收到数据的时候再按照相同的规则把字符串转化回结构体;
这就是序列化和反序列化!
为什么要转换成字符串在发送呢?
向上通过反序列化读取消息,向下通过序列化包装消息。而 TCP/UDP 不关心发送的是什么,都按照字符串进行传输!
2.2.重新理解 read、write、recv、send 和 TCP 为什么支持全双工

- 在任何一台主机上,TCP 连接既有发送缓冲区,又有接收缓冲区,所以,在内核中,可以在发消息的同时,也可以收消息,即全双工。
- 这就是为什么一个 TCP sockfd 读写都是它的原因。
- 实际数据什么时候发,发多少,出错了怎么办,由 TCP 控制,所以 TCP 叫做传输控制协议。这就体现控制!传输层的问题都是由 OS 自主来决定的!
TCP 能接受全双工的本质原因是因为 TCP 连接各有一对发送缓冲区和接收缓冲区。
TCP 发送数据的本质:将自己的发送缓冲区拷贝到接收方的接收缓冲区中!
通信的本质就是拷贝!!!
read、write、recv、send 本质是拷贝函数!
我们的 read 或者 recv 为什么会阻塞?
因为缓冲区中没有数据,阻塞的本质就是用户层在同步!
TCP 设计也是符合生产者消费者模型!因为发送缓冲区和接收缓冲区都是属于操作系统的,所以一定是临界资源!会有多个生产者,多个消费者!而 IO 发生阻塞也就是为了维护同步关系,保证缓冲区的正确使用!
接受的时候,我们还需要解决一个问题,保证我们拿到的是一个完整请求!
举个例子:对方的接收缓冲区写满了,对方一直不读,那么我们的发送缓冲区就积压了很多同样的请求,如果一次性刷新过去,对方就读取到多条信息;又或者只发送了一条请求的一半过去,那么接受方读取就读取一半了,就不可能进行反序列化!这个过程就叫面向字节流!!!
所以怎么保证读取的是一个完整的请求呢???
这就是经典的 TCP 的粘包问题了!
2.3.如何解决粘包问题
需要我们自定义协议出马了!(协议的再理解)
我们自己规定协议如下:



