第二章、数据的连接与断开(TCP三次握手和四次握手)

2.1 创建套接字

什么是套接字?在协议栈内部存放控制信息的内存空间可以看做为套接字的实体。套接字中记录了用于控制通信操作的各种控制信息,而协议栈就是根据这些控制信息来工作的。

应用程序调用socket申请创建套接字,首先协议栈会分配用于存放一个套接字所需的内存空间,然后写入初始状态。

需要将表示这个套接字的描述符告知应用程序,描述符相当于套接字的身份证号,在多个套接字的情况下,帮助协议栈进行区分。

2.2 连接服务器

连接的含义:通信双方交换控制信息,在套接字中记录这些必要信息并准备数据收发的一连串操作。

首先,我们需要把服务器的IP地址和端口号等信息告知协议栈。

客户端需要向服务器传达开始通信的请求。此外我们还需要一块用于收发数据存放的内存空间,称之为”缓冲区“。

关于在套接字中提到的控制信息,大致可以分为两类:

第一类是客户端和服务器相互联络时交换的控制信息(这些信息会被添加在客户端与服务器之间传递的网络包的开头,称之为“头部”)。

第二类是保存在套接字中,用来控制协议栈操作的信息。

连接操作的实际过程(TCP三次握手

    第一步是在TCP模块处创建表示连接控制信息的头部,通过TCP头部中的发送方和接收方端口号可以找到要连接的套接字。

    将头部的控制位的SYN比特设置为1,并且设置适当的序号和窗口大小。

    TCP头部设置好之后,TCP模块会将信息传递给IP模块并委托它进行发送。

    上述操作结束后,服务器的TCP模块也需要返回响应。需要在TCP头部设置发送方和接收方的端口号以及SYN比特,还需要将ACK控制位设为1,表示已经接收到响应的网络包。委托IP模块返回响应。

    相应地,客户端收到响应后,也需要将ACK比特设置为1并发回服务器,告知其响应已经收到。

2.3 收发数据

1、将HTTP请求消息交给协议栈

这里要注意的是,协议栈收到请求消息不是立刻进行发送,而是暂时存放在内部的发送缓冲区中。那么何时进行发送呢?

第一个判断要素:每个网络包能容纳的数据长度

第二个因素是发送时间(协议栈会有个计时器,到达一定时间就会发送)

2、由于HTTP消息的长度可能会很长,一个网络包放不下,那么就需要进行拆分。

3、使用ACK号确定网络包已收到。

因为网络包被拆分,所以发送的时候需要告知接收方数据从哪几个字节开始,长度是多少(这里的信息就是写在TCP头部中的“序号”字段中)。这里将SYN控制位设置为1表示连接,同时还需要设置序号字段的值(因为为了防止被窃取,序号并不是从1开始的),就是在这一步将序号的初始值告知对方的。相应的,接收方也需要返回一个响应,告知发送方已经收到多少数据,这个返回ACK号的操作称之为确定响应

4、如何解决返回ACK号的时间问题?

根据网络包平均往返时间调整ACK号等待时间

使用滑动窗口有效管理ACK

ACK号和窗口的合并

2.4 从服务器断开并删除套接字

1、数据发送完毕后断开连接(TCP断开时的四次握手)

以服务器先断开为例:

服务器一方:

首先服务器一方的应用程序会调用Socket库的close程序

然后,服务器的协议栈会生成包含断开信息的TCP头部,就是将控制位的FIN比特设置为1

协议栈委托IP模块向客户端发送数据

客户端一方:

收到服务器发来的FIN为1的TCP头部时,客户端的协议栈将自己的套接字标记为断开操作状态

向服务器返回一个ACK

协议栈告知应用程序来自服务器的数据都收到了,客户端应用程序调用close来结束数据收发操作

客户端也会生成一个FIN比特为1的TCP包,然后委托IP模块发送给服务器

一段时间后,服务器收到来自来自客户端的FIN之后,就会返回ACK

2、删除套接字

通信结束后,套接字不会立刻删除,为了防止误操作,会等待一段时间再删除。这里的误操作是因为当客户端返回的ACK号丢失了,服务器会重新发送FIN,就会发给新的套接字(因为套接字立刻删除后,端口号就会被释放),这样新的套接字就会被执行断开操作。

2.5 UDP协议的收发操作

虽然TCP传输十分可靠,但是过于复杂,在有些情况下,使用UDP效果更好。UDP不需要建立连接和断开的步骤,只是负责单纯地发送数据包而已。

下面的几个场景适合是使用UDP

    不需要重发的数据用UDP发送更高效

    控制信息中用的短数据

    音频和视频数据

来源url
栏目