前言
大家好,我是柒八九
。今天我们继续来讲述一下,针对网络通信方面的东西。
在前几篇文章
- 网络通信之生成HTTP消息
- 网络通信之IP地址
- 网络拾遗之 DNS协议:网络世界的地址簿
我们基本上从宏观角度描述了,应用层是如何构建通信消息、查询服务端IP地址的。今天,我们着重讲讲,在客户端准备好通信消息后,是如何委托OS的协议栈进行后续的处理。也就是,Socket如何处理从客户端拿到数据,并将其转发到协议栈。
简明扼要
- 套接字的实体就是「通信控制信息」
- 「协议栈」是根据套接字中记录的控制信息来工作的
- 「描述符」相当于用来区分协议栈中的「多个套接字」的号码牌
- 通信操作中使用的控制信息分为「两类」。 (1) 「头部」中记录的信息 (2) 「套接字」(协议栈中的内存空间)中记录的信息
- 「MTU」:一个网络包的最大长度,以太网中一般为 1500 字节
- 「MSS」:除去头部之后,一个网络包所能容纳的 TCP 数据的最大长度
❝「面试加油站」: 1. 「协议栈」是根据套接字中记录的控制信息来工作的 2.「描述符」相当于用来区分协议栈中的「多个套接字」的号码牌 3. 「MTU」:一个网络包的最大长度,以太网中一般为 1500 字节 4. 「MSS」:除去头部之后,一个网络包所能容纳的 TCP 数据的最大长度 ❞
文章概要
- 套接字(Socket)是个啥
- 保存控制信息的头部
- HTTP 请求消息交给协议栈
- 较大的数据进行拆分
1. 套接字(Socket)是个啥
在网络拾遗之 DNS协议:网络世界的地址簿我们介绍了,「Socket 库是用于调用网络功能的程序组件集合」。
使用 Socket 库来收发数据的操作过程
各端能够「准确无误」的进行通信,主要的功劳还是通过套接字来维护各个连接的状态和消息。那么我们就简单了解一下。
在「协议栈内部」有一块用于存放「控制信息」的内存空间,这里记录了用于控制通信操作的控制信息,例如通信对象的 IP 地址、端口号、通信操作的 进行状态等。
❝套接字的实体就是「通信控制信息」 ❞
套接字中记录了用于控制通信操作的各种控制信息,协议栈则需要根据这些信息判断「下一步」的行动,这就是「套接字的作用」。
「协议栈是根据套接字中记录的控制信息来工作的」
创建套接字时,首先分配一个套接字所需的「内存空间」,然后向其中写入初始状态。然后,将表示这个套接字的「描述符」告知应用程序。
❝「描述符」相当于用来区分协议栈中的「多个套接字」的号码牌 ❞
应用程序在向协议栈进行收发数据委托时,只要通过描述符确定了相应的套接字,协议栈就能够获取所有的相关信息。
关于套接字的细节还有很多,我们只需要知道,在双端通信的时候,是根据各自维护的套接字进行信息记录和状态切换的。这样,才可以有条不紊的进行数据交互。
2. 保存控制信息的头部
控制信息其实可以大体上分为两类。
「第一类」是客户端和服务器相互联络时交换的控制信息。这些信息不仅连接时需要,包括数据收发和断开连接操作在内,整个通信过程中都需要,这些内容在 TCP 协议的规格中进行了定义。
客户端与服务器之间交换的控制信息
控制信息还有「另外一类」,那就是「保存在套接字」中,用来控制协议栈操作的信息。应用程序传递来的信息以及从通信对象接收到的信息都会保存在这里,还有收发数据操作的「执行状态」等信息也会保存在这里,「协议栈」会根据这些信息来执行每一步的操作。
❝通信操作中使用的控制信息分为「两类」。 (1) 「头部」中记录的信息 (2) 「套接字」(协议栈中的内存空间)中记录的信息 ❞
3. HTTP 请求消息交给协议栈
协议栈并「不关心」应用程序传来的数据是什么内容,在协议栈看来,要发送的数据就是「一定长度的二进制字节序列」而已。
协议栈并不是一收到数据就马上发送出去,而是会将数据存放在内部的「发送缓冲区」中,并等待应用程序的下一段数据。
至于要积累多少数据才能发送,不能一概而论,但都是根据下面几个要素来判断的。
「第一个判断要素」是每个网络包能容纳的「数据长度」,协议栈会根据一个叫作 MTU
(Maximum Transmission Unit,最大传输单元) 的参数来进行判断。MTU 表示一个网络包的「最大长度」,在以太网中一般是 1500 字节。
MTU 是「包含头部的总长度」,因此需要从 MTU 减去头部的长度,然后得到的长度就是一个网络包中所能容纳的最大数据长度,这一长度叫作 MSS
(Maximum Segment Size,最大分段大小)。
当从应用程序收到的数据长度超过或者接近 MSS 时再发送出去,就可以避免发送大量小包的问题了。
MTU 与 MSS
❝「MTU」:一个网络包的最大长度,以太网中一般为 1500 字节。 「MSS」:除去头部之后,一个网络包所能容纳的 TCP 数据的最大长度 ❞
另一个判断要素是「时间」。当应用程序发送数据的「频率不高」的时候,如果每次都等到长度接近 MSS 时再发送,可能会因为等待时间太长而「造成发送延迟」,这种情况下,即便缓冲区中的数据长度没有达到 MSS,也应该果断发送出去。
这两个判断要素,但它们其实是「互相矛盾」的。如果仅靠协议栈来判断发送的时机可能会带来一些问题,因此协议栈也给应用程序保留了控制发送时机的余地。「应用程序」在发送数据时可以指定一些选项,比如如果指定“不等待填满缓冲区直接发送”,则协议栈就会按照要求直接发送数据。
4. 较大的数据进行拆分
HTTP 请求消息一般不会很长,一个网络包就能装得下,但如果其中要提交表单数据,长度就可能「超过一个网络包」所能容纳的数据量。
这种情况下,「发送缓冲区」中的数据就会「超过 MSS 的长度」,这时我们当然不需要继续等待后面的数据了。发送缓冲区中的数据会被「以 MSS 长度为单位进行拆分」,拆分出来的每块数据会被放进「单独」的网络包中。
根据发送缓冲区中的数据拆分的情况,当判断需要发送这些数据时,就在每一块数据前面「加上 TCP 头部」,并根据「套接字中记录」的控制信息标记发送方和接收方的「端口号」,然后交给 IP 模块来执行发送数据的操作。
序数据的拆分
自此,从客户端发送的消息,被封装成一个个网络包。然后,就会顺着通信通道进行传输。当然,我们这里只是从整个宏观角度,描述了一下,网络包的生成。其实,其中还涉及到 TCP/IP协议栈对网络包的处理。这个我们也会有对应的文章进行介绍。
后记
「分享是一种态度」,这篇文章,主要的篇幅来自于《网络是如何连接的》,算是一个自我学习过程中的一种记录和总结。主要是把自己认为重要的点,都罗列出来。同时,也是为大家节省一下排雷和踩坑的时间。当然,可能由于自己认知能力所限,有些点,没能表达的很好。
参考资料:
- 网络是如何连接的