企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## TCP报文格式 ![](https://img.kancloud.cn/1d/ba/1dbaf58e900c19808846f058298cdfac_399x176.png) ``` 上图中有几个字段需要重点介绍下: (1)序号:Seq序号,占32位,用来标识从TCP源端向目的端发送 的字节流,发起方发送数据时对此进行标记。 (2)确认序号:Ack序号,占32位,只有ACK标志位为1时,确认 序号字段才有效,Ack=Seq+1。 (3)标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN 等,具体含义如下: (A)URG:紧急指针(urgent pointer)有效。 (B)ACK:确认序号有效。 (C)PSH:接收方应该尽快将这个报文交给应用层。 (D)RST:重置连接。 (E)SYN:发起一个新连接。 (F)FIN:释放一个连接。 需要注意的是: (A)不要将确认序号Ack与标志位中的ACK搞混了。 (B)确认方Ack=发起方Req+1,两端配对。 ``` ![](https://img.kancloud.cn/05/09/0509a616f582143db516faefc9da4ee4_551x773.png) ## 三次握手 所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示: ![](https://img.kancloud.cn/57/06/57062942b4b01179b6b1d3b9a71a873a_648x418.png) ![](https://img.kancloud.cn/eb/ac/ebacc31277be76113eb61f3f93b392e5_784x867.png) ``` (1)第一次握手: Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包 发送给Server,Client进入SYN_SEND状态,等待Server确认。 (2)第二次握手: Server收到数据包后由标志位SYN=1知道Client请求建立连接, Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个 值seq=K,并将该数据包发送给Client以确认连接请求,Server进 入SYN_RCVD状态。 (3)第三次握手: Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则 将标志位ACK置为1,ack=K+1,并将该数据包发送给Server, Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立 成功,Client和Server进入ESTABLISHED(TCP连接成功)状态, 完成三次握手, 随后Client与Server之间可以开始传输数据了。 ``` ## SYN攻击 ``` 在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK 之前的TCP连接称为半连接(half-open connect),此时Server处 于SYN\_RCVD状态,当收到ACK后,Server转入ESTABLISHED 状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址, 并向Server不断地发送SYN包,Server回复确认包,并等待Client 的确认,由于源地址是不存在的,因此,Server需要不断重发直 至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常 的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫 痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非 常简单,即当Server上有大量半连接状态且源IP地址是随机的, 则可以断定遭到SYN攻击了,使用如下命令可以让之现行: `netstat -nap | grep SYN\_RECV` ``` ## 四次挥手 ![](https://box.kancloud.cn/c119cfe162443a991a3aeff7c29d2742_668x463.png) 第一次挥手:客户端发送一个FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入FIN\_WAIT\_1状态。意思是说"我客户端没有数据要发给你了",但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。 第二次挥手:服务器端收到FIN后,先发送ack=M+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入FIN\_WAIT\_2 状态,继续等待服务器端的FIN报文。 第三次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=N报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入LAST\_ACK状态。 第四次挥手:客户端收到FIN=N报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送ack=N+1后进入TIME\_WAIT状态,如果Server端没有收到ACK则可以重传。服务器端收到ACK后,就知道可以断开连接了。客户端等待了2MSL后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。  常见面试题 【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手? 答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。 【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态? 答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。 【问题3】为什么不能用两次握手进行连接? 答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。        现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。 【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办? TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。