计算机网络知识点(三)
总结一下计算机网络知识点,仅供个人学习。
前言
本文主要记录一下学习TCP的三次握手和四次挥手,以及面试时一些高频问题。
一、TCP报文段
先来简单认识一下 TCP 报文段的结构。
TCP 首部包含以下内容,请留意其中的控制位,在三次握手和四次挥手过程中会频繁出现:
- 源端口号 和 目的端口号 每个 TCP 报文段都包含源端口号和目的端口号,用于寻找发送端和接收端应用进程。这两个值加上 IP 首部中的源端 IP 地址 和 目的端 IP 地址 就可以确定一个唯一的 TCP 连接。
- 数据序号(Sequence Number 16位):表示在这个报文段中的第一个数据字节序号。
- 确认序号(Acknowledgment Number32位):仅当ACK标志为1时有效,确认号表示期望收到的下一个字节的序号。
- 部首长度(Header Length 4位):数据偏移,该字段的值是TCP首部(包括选项)长度除以4。
- 保留位(Reserved 6位):占 6 位,未来可能有具体用途,目前默认值为0。
- 控制位 (Control Bits 6位):在三次握手和四次挥手中会经常看到 SYN、ACK 和 FIN,一共有 6 个标志位,它们表示的意义如下:
URG (Urgent Bit):值为 1 时,紧急指针有效
ACK (Acknowledgment Bit):值为 1 时,确认序号有效
PSH (Push Bit):接收方应尽快将这个报文交给应用层
RST (Reset Bit):发送端遇到问题,想要重建连接
SYN (Synchronize Bit):同步序号,用于发起一个连接
FIN (Finish Bit):发送端要求关闭连接,结束标志 - 窗口字段(Window 16位):16位,代表的是窗口的字节容量,也就是TCP的标准窗口最大为2^16 - 1 = 65535个字节, TCP的流量控制由连接的每一端通过声明的窗口大小来提供。
- 校验和(Checksum 16位):功能类似于数字签名,用于验证数据完整性,也就是确保数据未被修改。检验和覆盖了整个 TCP 报文段,包括 TCP 首部和 TCP 数据,发送端根据特定算法对整个报文段计算出一个检验和,接收端会进行计算并验证。
- 紧急指针(Urgent Pointer 16位):当 URG 控制位值为 1 时,此字段生效,紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。 TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式。
- 选项(Options):这一部分是可选字段,也就是非必须字段,最常见的可选字段是“最长报文大小”。
- 有效数据部分 (Data):这部分也不是必须的,比如在建立和关闭 TCP 连接的阶段,双方交换的报文段就只包含 TCP 首部。
二、TCP三次握手和四次挥手
1.建立连接(三次握手)
怎样更好地去理解三次挥手,就比如小红给小明打电话。
小红:“小明,我有点事情想问你,你能听见吗?”
小明:“我能听到,你有什么事啊?你能听到我说话吗?”
小红:“我也能听到,那我下面开始问了。”
如图所示,双方间的三个蓝色箭头就代表了三次握手过程中所发生的数据交换。
(1)第一次握手:客户端给服务端发送请求信息(报文段1),并指定同步序号SYN=1,ACK=0,初始数据序号seq=x,同时TCP的客户端进程进入SYN-SENT(同步已发送)状态。
(2)第二次握手:服务端接收到客户端传来的请求后,向客户端发送响应信息(报文段2),其中SYN=1 表示这也是用于发起连接的报文段;ACK=1 表示对客户端作出应答,其确认序号字段(ack)生效,该字段值为ack=x+1,也就是从客户端收到的报文段的序号加一;数据序号seq=y 表示服务端期望下次收到客户端的数据序号。发送完报文段2后,服务器进入 SYN-RECEIVED 状态。
(3)第三次握手:客户端收到服务端传来的响应后,向服务端发送确认信息(报文段3),表示已经收到。其中ACK = 1 代表对服务端做出的应答;确认序号字段 ack=y + 1,序号字段 seq = x + 1。此报文段发送完毕后,双方都进入 ESTABLISHED 状态,表示连接已建立。
至此三次握手建立连接完毕。
(1)第一次握手时seq为什么是x?
第一次的seq序列号是随机产生的,这样是为了网络安全,如果不是随机产生初始序列号,黑客将会以很容易的方式获取到你与其他主机之间的初始化序列号,并且伪造序列号进行攻击。
(2)TCP 建立连接为什么而不是两次握手?
-
防止已过期的连接请求报文突然又传送到服务器,因而产生错误。
假如客户端第一次想服务端发送报文A请求建立连接,由于网络原因报文A暂时无法达到服务端,客户端在长时间没有收到服务端应答情况下,会重新发送请求报文B,服务端收到报文B后正常响应,双方建立连接后完成数据传输,最后关闭连接。假设这个时候,报文A到达服务端,那么服务端会认定这是一个新的连接请求,但是此时客户端已经关闭,所以服务端会一直等待下去,这将导致不必要的错误和资源的浪费。 -
三次握手才能让双方均确认自己和对方的发送和接收能力都正常。
两次握手并不足以让双方确认自己和对方的发送和接受能力全部正常。比如,客户端第一次发送连接请求(第一次握手),服务端做出了响应(两次握手完成),此时客户端知道自己的发送和接受能力正常,但是服务端只能够知道自己的接收能力正常,发送能力不确定。 -
告知对方自己的初始序号值,并确认收到对方的初始序号值
TCP 实现了可靠的数据传输,原因之一就是 TCP 报文段中维护了序号字段和确认序号字段,也就是图中的 seq 和 ack,通过这两个字段双方都可以知道在自己发出的数据中,哪些是已经被对方确认接收的。这两个字段的值会在初始序号值得基础递增,如果是两次握手,只有发起方的初始序号可以得到确认,而另一方的初始序号则得不到确认。
(3)TCP 建立连接为什么要三次握手而不是四次?
因为三次握手已经可以确认双方的发送接收能力正常,双方都知道彼此已经准备好,而且也可以完成对双方初始序号值得确认,也就无需再第四次握手了。
(4)SYN洪泛攻击(SYN Flood,半开放攻击),怎么解决?
洪泛攻击?
在三次握手过程中,服务器收到了客户端的SYN报文段后,会分配并初始化连接变量和缓存,并向客户端发送SYN+ACK报文段,这就相当于一个“半开连接”,会消耗服务器资源。如果客户端正常返回ACK报文段,那么双方可以进行正常连接,否则,服务器在等待一分钟后会终止这个“半开连接”并回收资源。这样的机制为SYN洪泛攻击提供了机会,这是一种经典的Dos攻击(拒绝服务攻击),所谓的拒绝服务攻击就是通过进行攻击,使受害主机或网络不能提供良好的服务,从而间接达到攻击的目的。在 SYN 洪泛攻击中,攻击者发送大量的 SYN 报文段到服务器请求建立连接,但是却不进行第三次握手,这会导致服务器打开大量的半开连接,消耗大量的资源,最终无法进行正常的服务。
解决方案:
检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击【在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击】。
- 缩短超时(SYN Timeout)时间
- 增加最大半连接数
- 过滤网关防护
- SYN cookies技术:SYN Cookies 是对 TCP 服务器端的三次握手做一些修改,专门用来防范 SYN 洪泛攻击的一种手段。它的原理是,在服务器接收到 SYN 报文段并返回 SYN + ACK 报文段时,不再打开一个半开连接,也不分配资源,而是根据这个 SYN 报文段的重要信息,包括源IP和目的 IP ,端口号和一个只有服务器自己知道的秘密函数,利用特定散列函数计算出一个 cookie 值。这个 cookie 作为将要返回的SYN + ACK 报文段的初始序列号(ISN)。如果客户端是正常连接,将会返回一个cookie+1的报文段,接下来服务器会根据确认报文的源 Id,目的 Id,端口号以及秘密函数计算出一个结果,如果结果的值 + 1 等于确认字段的值,则证明是刚刚请求连接的客户端,这时候才为该 TCP 分配资源
(5)TCP三次握手中,最后一次回复丢失,会发生什么?
-
如果最后一次ACK在网络中丢失,那么服务端该TCP连接的状态仍为SYN_RECV,并且根据
TCP的超时重传机制依次等待3秒、6秒、12秒后重新发送 SYN+ACK 包,以便 客户端重新发送ACK包 -
如果重发指定次数后,仍然未收到ACK应答,那么一段时间后,服务端自动关闭这个连接
-
但是客户端认为这个连接已经建立,如果客户端端向服务端发送数据,服务端将以RST包(Reset,标示复位,用于异常的关闭连接)响应,此时,客户端知道第三次握手失败
2.断开连接(四次挥手)
继续以上面打电话的情景来简单地说一下四次挥手。
小红:“好了,我的问题已经问完了,你还有要说的吗,没有的话我就挂电话了。”
小明:“稍等,我还有些话没说完”
( 。。。)
小明:“好了,就这些了,你可以挂电话了。”
小红:“好的。”
然后熊红和小明就挂断了电话。
建立一个连接需要三次挥手,而终止一个连接要经过四次握手。这有TCP的半关闭造成的,TCP连接是全双工的,因此每个方向必须单独的进行关闭。
四次挥手的详细过程如下:
(1)第一次挥手:客户端发送关闭连接的请求(报文段1)。其中 FIN=1 代表请求关闭连接,并停止发送数据,序号字段 seq=x (等于上次发送数据的最后一个字节序号加一),然后客户端进入 FIN-WAIT-1 状态,等待服务端的确认报文。
(2)第二次挥手:服务端收到客户端的关闭连接请求后,发送确认信息(报文段2)。ACK=1 代表确认收到客户端发来的序号,ack=x+1 代表将客户端发过来的序号加一,返回给客户端供其确认,并且带上自己的序号 seq=y ,然后服务端进入CLOSE-WAIT状态。此时,服务器还会通知上层程序客户端已经释放连接,TCP处于半关闭状态,表明客户端已经没有数据要发送了,但是服务器还可以继续发送数据,客户端也还能够接收。
客户端收到服务端的确认报文后,进入 FIN-WAIT-2状态 此时还能够接收服务端的数据,直到收到服务端发送的 FIN 报文段。
(3)第三次挥手:服务端发送完数据后,向客户端发送断开连接信息(报文段3)。FIN=1 代表服务端请求断开连接,ACK=1 、ack=x+1 代表确认收到上次客户端发送的序号, 带上自己的序号 seq=z 。随后,服务器进入 LAST-ACK状态 等待来自客户端的确认报文段。
(4)第四次挥手:客户端收到服务端的断开连接请求后,返送确认信息(报文段4)。ACK=1 、ack=z+1 代表确认收到服务端的序号并返回确认序号,seq=x+1 代表客户端没有重新生成新的数据序号。随后客户端进入 TIME-WAIT状态 ,等待2MSL(2 * Maximum Segment Lifetime,两倍的报文段最大存活时间),这是任何报文段在被丢弃潜能在网络中存在的最长时间,常用值30S、1MIN和2MIN。此时间过后,客户端进入 CLOSED状态 。
服务器在收到客户端的确认信息后,随即进入 CLOSED状态 ,由于没有等待时间,服务器比客户端跟早进入CLOSED状态。
(1) TCP 关闭连接为什么要四次挥手而不是三次?
服务器在收到客户端的 FIN 报文段后(第一挥手),可能还有数据要传输,所以不能立刻关闭连接,但是会做出应答(第二次回收),接下来可能会继续发送数据,在数据发送完后,服务器会向客户单发送 FIN 报文(第三次挥手),表示数据已经发送完毕,请求关闭连接,然后客户端再做出应答(第四次挥手),因此一共需要四次挥手。
(2)为什么连接的时候是三次握手,关闭的时候却是四次握手?
- 建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
- 关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以服务器可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接。因此,服务器ACK和FIN一般都会分开发送,从而导致多了一次。
(3)为什么客户端最后还要等待2MSL?为什么还有个TIME-WAIT的时间等待?
-
保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,服务器已经发送了FIN+ACK报文,请求断开,客户端却没有回应,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。
-
防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,这样新的连接中不会出现旧连接的请求报文。
-
2MSL是最大报文生存时间,一个MSL 30 秒,2MSL = 60s
(4)为什么TCP挥手每两次中间有一个 FIN-WAIT等待时间?
主动关闭的一端调用完close以后(即发FIN给被动关闭的一端, 并且收到其对FIN的确认ACK)则进入FIN_WAIT状态。如果这个时候因为网络突然断掉、被动关闭的一段宕机等原因,导致主动关闭的一端不能收到被动关闭的一端发来的FIN(防止对端不发送关闭连接的FIN包给本端),这个时候就需要FIN_WAIT定时器, 如果在该定时器超时的时候,还是没收到被动关闭一端发来的FIN,那么直接释放这个链接,进入CLOSE状态。
总结
TCP的连接和断开过程其实是非常复杂的,本人只学习了一下基础性知识以及面试问题,如有错误,请大家指正!
参考链接
https://www.zhihu.com/question/271701044/answer/1808988570
https://juejin.cn/post/6844904194764177416
https://segmentfault.com/a/1190000022410446
https://www.nowcoder.com/discuss/568071?from=zhnkw