声明:以下内容为学习刘超老师的极客时间专栏——《趣谈网络协议》时的学习笔记,不代表刘超老师的任何个人观点,如描述有误,可在评论中指出。同时,通过这门课程也学到了很多知识,刘超老师讲得通俗易懂,如果有需要的话,可自行订阅学习,直达链接。
网络协议 —— TCP&UDP
TCP
什么是 TCP?
TCP 的全称是 Transmission Control Protocol,传输控制协议。
通过三次握手来建立 TCP 连接,三次握手就是用来启动和确认 TCP 连接的过程。一旦连接建立后,就可以发送数据了,当数据传输完成后,会断开连接。
主要特点:
- 能够确保连接的建立和数据包的发送;
- 支持错误重传机制;
- 支持拥塞控制,能够在网络拥堵的情况下延迟发送;
- 能够提供错误校验和,甄别有害的数据包;
- 支持流量控制、按序传输;
- 能够维护连接。
TCP 包头格式
- 源端口号、目标端口号和 UDP 一样必不可少,有了这两个端口号,数据才知道应该发个哪个应用。
- 序号。即包的序号,通过给包编号来解决乱序问题,达到按序传输的目的。
- 确认序号。发出去的包应该有确认,如果没有收到就应该重新发送,直到送达。这个可以解决不丢包的问题。
- 状态位。如 SYN 是发起一个连接、ACK 是回复、RST 是重新连接、FIN 是结束连接等。TCP 面向连接,双方需要维护连接的状态,发送这些带状态位的包时,会引起双发的状态变更。
- 窗口大小。TCP 要做流量控制,通信双方各声明一个窗口,标识自己当前能够处理的能力。
TCP 三次握手
三次握手相关名词释义
消息类型 | 描述 |
---|---|
SYN | 这个消息是用来初始化和建立连接的 |
ACK | 帮助对方确认收到的SYN消息 |
SYN-ACK | 本地的SYN消息和较早的ACK数据包 |
FIN | 用来断开连接 |
- SYN:它的全称是 Synchronize Sequence Numbers,同步序列编号。是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立 TCP 连接时,首先会发送的一个信号。客户端在接受到 SYN 消息时,就会在自己的段内生成一个随机值 X。
- SYN-ACK:服务器收到 SYN 后,打开客户端连接,发送一个 SYN-ACK 作为答复。确认号设置为比接收到的序列号多一个,即 X+1,服务器为数据包选择的序列号是另一个随机数 Y。
- ACK:Acknowledge character,确认字符,表示发来的数据已确认接收无误。最后,客户端将ACK发送给服务器。序列号被设置为所接收的确认值即 Y+1.
TCP 建立连接时需要经过三次握手,其状态时序图:如下:
- 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。
- 客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。
- 客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。
- 服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。
用现实生活来举例的话就是(小明-客户端,小红-服务端)
5. 小明给小红打电话,接通了后,小明说喂,能听到吗,这就相当于是连接建立
。
6. 小红给小明回应,能听到,你能听到我说的话吗,这就相当于是请求响应
。
7. 小明听到小红的回应后,说,可以的,这相当于是连接确认
。
在这之后小明和小红就可以通话/交换信息了。
为什么需要三次握手?
三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。
- 第一次握手:Client 什么都不能确认;Server 确认了对方发送正常;
- 第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己接收正常,对方发送正常;
- 第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送接收正常。
所以三次握手就能确认双发收发功能都正常,缺一不可。
TCP 四次挥手
TCP 断开连接时需要经过四次挥手,其状态时序图:如下:
- 首先,客户端应用程序决定要终止连接(这里服务端也可以选择断开连接)。这会使客户端将 FIN 消息发送到服务器,并进入
FIN_WAIT_1
状态。当客户端处于 FIN_WAIT_1 状态时,它会等待来自服务器的 ACK 响应。 - 然后第二步,当服务器收到 FIN 消息时,服务器会立刻向客户端发送
ACK 确认消息
。 - 当客户端收到服务器发送的 ACK 响应后,客户端就进入
FIN_WAIT_2
状态,然后等待来自服务器的 FIN 消息。 - 服务器发送 ACK 确认消息后,一段时间(可以进行关闭后)会发送 FIN 消息给客户端,告知客户端可与进行关闭。
- 当客户端收到从服务器发送的 FIN 消息时,客户端就会由 FIN_WAIT_2 状态变为
TIME_WAIT
状态。处于 TIME_WAIT 状态的客户端允许重新发送 ACK 到服务器为了防止信息丢失。客户端在 TIME_WAIT 状态下花费的时间取决于它的实现,在等待一段时间后,连接关闭,客户端上所有的资源(包括端口号和缓冲区数据)都被释放。
补充说明:等待的时间设为 2MSL,MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL 域,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。协议规定 MSL 为 2 分钟,实际应用中常用的是 30 秒,1 分钟和 2 分钟等。
为什么要设置等待时间即 TIME_WAIT 状态?
(这里 A 代表上图中左边的一端,B 代表上图中右边的一端)
- 预留足够时间以重新发包,确认连接断开。A 收到 B 的 FIN 时,会从 FIN_WAIT_2 状态结束,然后再发一个 ACK 给 B。TCP 协议要求 A 最后等待一段时间 TIME_WAIT,是为了防止 B 没有收到 ACK时,有足够的时间可以重新发送 FIN,A 再次收到 FIN 并重新发送 ACK 有足够时间到达 B。
- 防止混乱。A 从 FIN_WAIT_2 状态结束时,A 的端口就直接空出来了,但 B 原来发过的很多包很可能还在路上,如果 A 的端口被一个新的应用占用了,这个新的应用会收到上个连接中 B 发过来的包,虽然序列号是重新生成的,但是这里要上一个双保险,防止产生混乱,因而也需要等足够长的时间,等到原来 B 发送的所有的包都完成了,再空出端口来。
- B 超过了 2MSL 的时间依然没有收到它发的 FIN 的 ACK,按照 TCP 的原理,B 会重发 FIN,A 再收到这个包就直接发送 RST,B 就知道 A 早就断开连接了。
还是可以用上面那个通过的例子来进行描述
- 小明对小红说,我所有的东西都说完了,我要挂电话了。(发送 FIN 消息到服务器,进入 FIN_WAIT_1 状态,等待来自服务器的 ACK 响应即等待来自小红的确认响应)
- 小红说,收到,我这边还有一些东西没说。(服务器收到 FIN 消息,向客户端发送 ACK 确认消息,客户端进入 FIN_WAIT_2 状态。)
- 经过若干秒之后,小红也说完了,小红说,我说完了,现在可以挂断了。(发送 FIN 消息给客户端,告知客户端可以关闭)
- 小明收到消息后,又等了若干时间后,挂断了电