TCP连接
概述
- TCP是一种面向连接、可靠的、基于字节流的通信协议,数据在传输前要建立连接,传输完毕后需要关闭连接
- 客户端在收发数据前要使用connect()函数和服务器建立连接。建立连接的目的是保证IP地址、端口、物理链路正确无误,为数据的传输开辟通道
- TCP建立连接的时候需要传输三个数据包,俗称三次握手
TCP中的三次握手和四次挥手是什么?
- tcp的三次握手主要是指tcp客户端和服务端建立连接的过程,四次挥手即断开连接的过程;
- 三次挥手(占用资源),四次挥手(释放资源)
三次挥手:(TCP建立连接需要传输三个数据包,建立连接)
- 客户端的 connect 默认是阻塞的,只有三次握手成功,连接建立后才会接触阻塞
- 第一次:客户端发送数据给服务端,客户端要连接服务器,通知服务器准备好资源;
- 第二次:服务端发送数据给客户端,通知客户端其已经准备好了资源,让客户端也准备好资源;
- 第三次:客户端收到通知,确认双方都准备好了,建立连接。
四次挥手:(关闭连接)
- 第一次:客户端关闭发送数据,通知服务端,我要关闭发送了;
- 第二次:服务端发送数据给客户端,我已收到数据,我也要关闭接收数据了;
- 第三次:服务器发送数据给客户端,我要关闭发送数据了;
- 第四次:客户端发送数据给服务端,我已接收到数据,我也关闭接收数据了。
TCP数据报结构
- TCP数据报结构

- 序列号seq:占4个字节,用来标记数据段的顺序,tcp把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号;序列号seq就是这个报文段中的第一个字节的数据编号。
- 确认号ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号;序列号表示报文段携带数据饿第一个字节的编号;而确认号指的是期望接收到下一个字节的编号;因此当前报文段最后一个字节的编号+1,就是确认号。
- 确认ACK:占一位,仅当ACK=1时,确认号字段才有效;ACK=0时,确认号无效。
- 同步SYN:连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段。如果同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建立连接时才会被置为1,握手完成后SYN标志位被置为0。
- 终止FIN:用来释放一个连接。FIN=1表示:此报文段发送方的数据已发送完毕,并请求释放运输连接。
- ACK、SYN和FIN这些大写的单词表示标志位,其值要么是1,要么是0;ack、seq小写的单词表示序号。(1byte(字节) = 8bit(位))
- 客户端和服务端都可以发送确认号,ack=seq+1
- 标志位共有6个:URG,ACK,PSH,RST,SYN,FIN
- URG:紧急指针(urgent pointer)有效
- ACK:确认序号有序
- PSH:接收方应该尽快将这个报文交给应用层
- RST:重置连接
- SYN:建立一个连接
- FIN:断开一个连接

三次握手🤝
过程
- 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,同时进入SYN_SENT状态,等待服务器确认;
- SYN:同步序列编号(Synchronize Sequence Numbers)
- 第二次握手:服务器收到syn包,必须确认客户端SYN(ack=j+1),同时服务端也会发送一个syn包(syn=k),也就是SYN+ACK包,并为这次连接分配资源,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器发送过来的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),并分配资源,此时包发送完毕,客户端和服务端进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

- 白话讲就是:
- 1.请求建立连接,并发出序号;
- 2.server收到信号,有确认号(ACK),此时并同样返回请求序号seq;
- 3.client收到信号,有确认号(ACK),连接已经建立。
关键
- 三次握手的关键是要确认对方是否收到了自己的数据包,这个目标就是通过确认号ack字段实现的。计算机会记录发送的数据包的序号seq,等收到对方的数据包后,检测确认号ack字段,看
ack=seq+1是否成立,如果成立说明对方正确收到了自己的数据包。
小结
- 3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
四次挥手👋
过程
- 第一次挥手:客户端发送连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传过来的数据的最后一个字节的序号+1);此时,客户端进入FIN-WAIT-1(终止-等待-1)状态。
- TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
- 第二次挥手:服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v;此时,服务端进入了CLOSE-WAIT(关闭-等待状态)。
- TCP服务器去通知对应的应用程序,客户端向服务器的方向就释放掉了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端仍要接受。这个状态会持续一段时间,也就是整个CLOSE—WAIT状态持续的时间
- 第三次挥手:客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止-等待-2状态),等待服务器发送连接释放报文,在这之前还需要接受服务器发送的最后数据。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能有发送了一些数据,假定此时的序列号是seq=w,此时服务器就进入了LAST-ACK(最后确认状态),等待客户端的确认。
- 第四次挥手:客户端收到服务端的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待状态)。
- 注意此时TCP连接还没有释放,必须经过2**MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认报文,立即进入CLOSED状态。同时撤销TCB后,就结束了这次的连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

- 白话讲就是:
- 1.客户端申请断开连接即FIN (我这边准备断开连接了)
- 2.服务端接收信息返回,表示我已经接收到 (收到,请稍等,我这边准备一下)
- 3.服务端发送信息表示可以断开连接 (我准备好了,你可以断开连接了)
- 4.客户端接受信息,同时返回信息通知服务端自己收到信息,开始断开 连接(好的,拜拜!)
- 数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。
面试问题
为什么连接的时候是三次握手,关闭的时候却是四次握手?
- 因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。
- 但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。
- 只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
TCP的三次握手一定能保证传输可靠吗?不能
- 三次握手比两次更可靠,但也不是完全可靠,而追加更多次握手也不能使连接更可靠了。因此选择了三次握手。
- 世界上不存在完全可靠的通信协议。从通信时间成本空间成本以及可靠度来讲,选择了“三次握手”作为点对点通信的一般规则。
为什么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连接。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
- TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器.
- 时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接
为什么必须是三次握手,两次不可以吗?
- 如果只有两次握手,这个时候客户端没有回应,会浪费服务器的资源。
- 在第一次通信中,客户端向服务端发送信息,服务端收到信息后可以确认自己的收信能力和客户端的发信能力;
- 在第二次通信中,服务端向客户端发送信息后,客户端可以确认自己的发信能力和服务端的收信能力没有问题,但是服务端不知道自己的发信能力如何,所以就需要第三次握手通信;
- 在第三次通信中,客户端向服务端发送信息之后,服务端就可以确认自己的发信能力没有问题。
- 3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。