面试情景
一天,你进入了一个大厂面试。坐立不安之中,一个秃头中年男子,穿着一个发灰了的格子衬衫,戴着一副镜片厚9mm的眼镜,稳如磐石突然朝着你说到:“就是你这个小毛头来面试吧。”
心里一惊,这怕不是神仙级架构师。但还是故作镇定:“面试官您好,我是xxx…”
面试过程中……面试官随手抛来一句:“简单说说 TCP 三次握手吧。”。
简单说说???嗯,面试官人还不错。
再加上我面试前的精心准备,和饱读诗书的才华,和挥斥方遒的潇洒帅气,一定能深深折服他。
于是:“ TCP 三次握手很简单,
- 客户端首先生成一个 SYN 报文,随机生成一个 seq,发给服务端。
- 服务端生成一个 SYN 报文,随机生成一个 seq,返回一个 ACK(为客户端的 seq + 1),发给客户端。
- 客户端再返回一个ACK(为服务端的 seq + 1)和 seq(为先前的 seq + 1,也是服务端返回的 ACK 的值)。
三次握手就完成了。”
面试官再问:
- SYN 是啥
- seq 是啥
- ACK 是啥
- 为啥返回的 ACK = seq + 1
- TCP 3 次握手可以携带数据吗
- 为啥一定要 “3” 次,2 次不行吗
- 为啥断开要 4 次,不能 3 次吗
- 如果报文段丢失怎么办
- 报文超长延迟怎么办
- 网络拥塞怎么办
这都啥?????
“你回去等通知吧。”
<瑟瑟发抖>
下面进入正题,如果上面的题你都理解,能够熟练回答,那对于 TCP 原理你了解得已经比较透彻了。
背三次握手的过程可能很简单,但是面试官细问,那你就露馅了,所以得深入理解才行。
要了解 TCP 的三次握手,你首先需要了解 TCP 的通信原理。
(以下所有图片摘自《计算机网络 自顶向下方法》)
我们先看 TCP 报文段结构(不用全部都理解)
我们只需要关注到,其中有
- 首部(只需要知道有这个东西)
- 序号字段 (32比特)
- 确认号字段 (序号和确认号用来实现:传输可靠)(32比特)
下面进行深入解释
为了实现通信的可靠,就需要一方对另一方的发送的数据进行:回复确认。
- 比如:
- A 对 B 说,我要去你家啦。
(这时 A 还不知道 B 有没有收到 A 要过去的消息,所以他不能随意去别人家) - (直到)B 对 A 说,好的,请来吧。
(这时 B 就可以确认 A 已经收到了消息,他就可以放心的过去了)
这就是 TCP 来实现通讯可靠的原理。
在 TCP 通讯中。
- 举个例子
- A 发送了一条数据报文,Seq = 100,data 长度为 20
- B 接收成功,返回的 ACK = 120。
(返回的 ACK = 发送的 Seq + data 长度) - 如果 A 要再次发送,则 Seq = 120。
(根据收到的 ACK = 120 可得知数据发送序号已经到 120 了,便可以接着发送)
这里返回的 ACK = 发送的 Seq + data 的长度。(用来表示全部收到)
在 TCP 中,两边都是有 Seq 和 ACK 和各自的 data 的。
- 以《计算机网络 自顶向下方法》书中的例子来说明
- A 和 B 都发送了一个一个字符 ‘ C ’ 给对方(也就是长度为 1 )
- A 发送 Seq = 42,ACK = 79,data = ‘ C ’
- B 接收了成功之后,返回给 B 的 ACK = 43,data = ‘ C ’ 自己的 Seq = 79
(因为 A 返回给他的 ACK = 79,说明 B 之前的发送序号已经达到到了 79,所以 Seq = 79) - A 此时没有数据要发送,所以返回时,只需要设置 ACK = 80,Seq = 43。
(返回时可以不带数据,ACK 用来确认,Seq 只是必须包含在内而已)
再举个例子 - A 发送了三条报文给 B
- Seq = 92,数据有 8 个字节
- 那第二条 Seq 一定 = 100,此时数据有 20 个字节
- 第三条 Seq = 120,数据有 30 个字节
- A 等待 B 返回的 ACK
- B 返回一条 ACK = 100,一条 ACK = 120,一条 ACK = 150
- 此时,正常情况下会收到三条 ACK 的返回。
不过 A 只需要收到一条 ACK = 150 即可
(ACK = 150 表示所有的数据都已经收到) - 可能网络各种原因,导致丢失报文。
比如 ACK = 100,ACK = 120, 丢失。但是 ACK = 150 收到,则可以确认数据全部传输成功。
若只收到 ACK = 120,则表示前两条数据传输成功,第三条报文出现问题。
现在,我们便已经明白了 TCP 传输的原理。
而 TCP 建立连接的过程,仅仅是发送特殊的报文来表示而已。
所以很容易可以明白。
TCP 三次握手过程
简单的说就是:
- 客户端向服务端发起连接请求
- 服务端回复确认
- 服务端向客户端发起连接请求
- 客户端回复确认
乍一看好像有 4 步,实际上,2、3两步可以成为一步。
服务端回复消息的时候,可以既确认客户端的请求,也发起自己的请求。
所以就是三次握手了
但跟面试官不能只说得这么扯皮,下面说得严谨一些
- 客户端 先向 服务端 发送一条特殊的报文段
其中不包含应用层数据
!在报文段首部中的一个标志位(即 SYN 比特)被置为 1(SYN = 1)
Seq = 随机选择一个初始序号 client_isn - 服务端 从收到的报文中提取出 SYN 比特位,发现这是一条请求连接的报文
于是为该 TCP 连接分配缓存和变量
返回给 客户端 的报文也不能包含应用层数据
报文的 SYN 比特也设置为 1(SYN = 1)
ACK = client_isn + 1(也就是 C 发来的 Seq + 1)
Seq = server_isn(服务器自己的初始序号) - 客户端 收到 服务端 发来的报文
给该链接分配缓存变量
返回报文给服务器表示确认,ACK = server_isn + 1。
SYN = 0,表示连接成功(以后的 SYN 都为 0)
这时的报文已经可以包含应用层数据了
四次挥手
同理,要理解四次挥手也很简单。
- 客户端向服务端发起分手请求
- 服务端回复确认
- 服务端向客户端发起分手请求
- 客户端回复确认
???
为啥 2、3 不能放在一块做 3 次 “分手”呢
实际上也是可以的。
只是因为一般来说,服务端在回复确认了中断连接的报文之后,很可能还有数据没有发送完。只有等到要发送给 “ 心爱的ta ” 的数据都发送完了之后,才会发送 “ 分手 ” 请求。
所以,先给对方回一个确认,然后过一会发送完数据,再发送中断连接报文。
中断报文则是 FIN 报文。如图:
现在我已经理解了 TCP 的通讯原理了。
但是还有问题没有解决。
比如:
- 如果 A 给 B 发了报文,但是 B 一直没收到?
- 或者 A 给 B 发了多个报文,但 B 只收到一部分?
- 或者 A 给 B 发了报文,隔了很久很久 B 才收到?
- 或者 A 给 B 发了报文,B 的回信丢失了?
那就需要重新发送,规则是
根据发送时间,计算是否超时
根据 B 返回的 ACK 判断,ACK 为多少,则代表 B 确认了多少,A 就要让 Seq = ACK,重新发送从该位置起,之后的数据
假设 A 给 B 发送了报文,Seq = 92,包含 8 个字节数据
那么 A 需要 B 返回的 ACK = 100 来确认
假设 B 的确认报文丢失了,或者传输时间过长。
那么 A 等了半天一直没有收到确认,则会将消息重新发送。
B 收到第二条一样的报文,知道了 A 没有得到确认,则会再次返回 ACK 来确认
假设 A 发送了两个报文
Seq = 92,8 个字节
Seq = 100,20 个字节
那么 A 就在等待 ACK = 100 和 ACK = 120 的报文。
可是 A 等了半天没反应,准备重发。
A 发送 Seq = 92,8 个字节
然后 A 就收到了 ACK = 100 和 ACK = 120
这时,第二条报文就不用再重发,已经保证了数据全部传输成功
而 B 收到重发的请求,虽然是第一条,但是仍需要发 ACK = 120 告知全部完成
(感觉还是看图最清楚,语言解释反而麻烦)
假设 A 发送的两段报文
Seq = 92,8 个字节
Seq = 100,20 个字节
那么 A 就在等待 ACK = 100 和 ACK = 120 的报文。
但是 ACK = 100 的报文丢失
不过 ACK = 120 到达则代表了 120 以前的全部成功(所以 ACK = 100 丢了无所谓)