TCP(Transmission Control Protocol)是面向连接的通信协议,通过三次握手建立连接, 然后才能开始数据的读写,通讯完成时要拆除连接,由于 TCP 是面向连接的所以只能用于端 到端的通讯。
其提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好两端之间 的准备工作。
所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认 连接的建立。在 socket 编程中,这一过程由客户端执行 connect 来触发。
TCP三次握手过程图:
TCP标志位,有6种标识:
① SYN(synchronous建立联机) ② ACK(acknowledgement 确认) ③ PSH(push传送) ④ FIN(finish结束)
⑤ RST(reset重置) ⑥ URG(urgent紧急) Sequence number(顺序号码 seq) / Acknowledge number(确认号码 ack)
1. 第一次:客户端将标志位 SYN 置为 1,随机产生一个值 seq=x,并将该数据包发送
给服务器端,客户端进入 SYN_SENT 状态,等待服务器端确认。
2. 第二次:服务器端收到数据包后由标志位 SYN=1 知道客户端请求建立连接,服务
器端将标志位 SYN 和 ACK 都置为 1,ack=x+1,随机产生一个值 seq=y,并将该数据包发送给客户端以确认连接请求,服务器端进入 SYN_RCVD 状态。
3. 第三次:客户端收到确认后,检查 ack 是否为 x+1,ACK 是否为 1,如果正确则将
标志位 ACK 置为 1,ack=y+1,并将该数据包发送给服务器端。服务器端检查 ack 是否为 y+1,ACK 是否为 1,如果正确则连接建立成功,客户端和服务器端进入 ESTABLISHED 状态。完成三次握手后客户端与服务器端之间可以开始传输数据了。
面试问题拓展:
1. 为什么非要是三次握手?不能是两次or四次?
答:TCP 是可靠的传输控制协议,而三次握手是保证数据可靠传输又能提高传输效率的最小次数。
三次握手的过程即是通信双方相互告知序列号起始值,并确认对方已经收到了序列号 起始值的必经步骤。
如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列 号则得不到确认。 至于为什么不是四次,很明显,三次握手后,通信的双方都已经知道了对方序列号起始值,也确认了对方知道自己序列号起始值,第四次握手已经毫无必要了。
2. 三次握手的有没有漏洞?
答:SYN洪泛攻击:三次握手中有一个第二次握手,服务端向客户端应答请求, 应答请求是需要客户端 IP 的,攻击者就伪造这个 IP,往服务器端狂发送第一次握手的内容, 当然第一次握手中的客户端 IP 地址是伪造的,从而服务端忙于进行第二次握手但是第二次 握手当然没有结果,所以导致服务器端被拖累,死机。
解决方案:
A. 无效连接监视释放
这种方法不停监视所有的连接,包括三次握手的,还有握手一次的,反正是 所有的,当达到一定(与)阈值时拆除这些连接,从而释放系统资源。这种方法对 于所有的连接一视同仁,不管是正常的还是攻击的,所以这种方式不推荐。
B. 延缓 TCB 分配方法
一般的做完第一次握手之后,服务器就需要为该请求分配一个 TCB(连接控制资源),通常这个资源需要 200 多个字节。延迟 TCB 的分配,当正常连接建立起来后再分配 TCB 则可以有效地减轻服务器资源的消耗。
C. 使用防火墙(最有效)
防火墙在确认了连接的有效性后,才向内部的服务器(Listener)发起 SYN 请求。
TCP四次挥手
四次挥手即终止 TCP 连接,就是指断开一个 TCP 连接时,需要客户端和服务端总共发 送 4 个包以确认连接的断开。在 socket 编程中,这一过程由客户端或服务端任一方执行 close 来触发。
由于 TCP 连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当主机A完成数据发送任务后,发送一个 FIN 给主机B来终止这一方向的连接;主机B收到一个 FIN 只是意味着不会再收到主机A的数据了,但是主机B依然可以给主机A发送数据,直到这主机B也发送了 FIN 给甲方。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭
过程图:
1. 第一次:主机A应用进程首先调用 close,我们称该端执行主动关闭(active close)。该端的 TCP 于是发送一个 FIN 分节,表示数据发送完毕,应用进程进入 FIN_WAIT_1(终止等待 1)状态。
2. 第二次:接收到这个 FIN报文段 的对端执行被动关闭(passive close),也就是主机B,发出确认报文。这个 FIN 由 TCP 确认,因为 FIN 的接收意味着接收端(主机B)应用进程在相应连接上再无额外数据可接收。接收端(主机B)进入了 CLOSE-WAIT(关闭等待)状态,这时候处于半关闭状态,即主动关闭端(主机A)已经没有数据要发送了,但是被动关闭端(主机B)若发送数据,主动关闭端(主机A)依然要接受。这个状态还要持续一段时间,也就是整个 CLOSE-WAIT 状态持续的时间。主动关闭端(主机A)收到确认报文后进入 FIN-WAIT-2 (终止等待 2)状态。
3. 第三次:一段时间后,主机B的应用进程将调用 close 关闭它的套接字,其 TCP 也 发送一个 FIN报文。
4. 第四次:主机A (主动关闭端)确认由主机B发送的FIN报文后,也发送一个 ACK报文,并进入了 TIME-WAIT(时间等待)状态。注意此时主机A的 TCP 连接还没有释放, 必须经过 2∗MSL(最长报文段寿命/最长分节生命期 max segement lifetime,MSL 是任何 IP 数据报能够在因特网中存活的最长时间,任何 TCP 实现都必须为 MSL 选择一个值。RFC 1122[Braden 1989]的建议值是 2 分钟,不过源自 Berkelcy 的实现传统上改用 30 秒这个值。 这意味着 TIME_WAIT 状态的持续时间在 1 分钟到 4 分钟之间)的时间后,当主机A撤销相应的 TCB 后,才进入 CLOSED 状态。
5. 被动关闭端只要收到了客户端发出的确认,立即进入 CLOSED 状态。同样,撤销 TCB 后,就结束了这次的 TCP 连接。可以看到,被动关闭端结束 TCP 连接的时间要比主动关闭端早一些。
面试问题拓展:
1. 既然每个方向都需要一个 FIN 和一个 ACK,因此通常需要 4 个分节。我们使用限定词“通常”是因为:某些情形下步骤 1 的 FIN报文会随数据一起发送;另外,步骤 2 和步骤 3 发送的分节 都出自执行被动关闭那-一端,有可能被合并成一个分节。
2. 为什么TCP挥手需要四次?
答:TCP 是全双工的连接,必须两端同时关闭连接,连接才算真正关闭。 如果一方已经准备关闭写,但是它还可以读另一方发送的数据。发送给 FIN 结束报文给对方,对方收到后,回复 ACK 报文。当这方也已经写完了准备关闭,发送 FIN 报文,对方回复 ACK。两端都关闭,TCP 连接正常关闭。也就是说:在上图 主机B 发送 ACK报文给主机A后,主机B可能还有数据尚未发送完,会继续发送给主机A。当数据发送完成后,主机B会再发出一个 FIN报文给主机A。
3. 为什么需要 TIME-WAIT 状态?
答:
a. 可靠的终止 TCP 连接。
根据前面的四次握手的描述,主机A收到服务器的连接释放的 FIN 报文后,必须发出确认ACK报文。若最后主机A发送的 ACK 确认报文丢失,那么主机B没有收到这个 ACK 确认报文, 就要重发 FIN 连接结束报文,主机A要在TIME-WAIT状态等待这个 FIN 连接结束报文段然后回复确认报文段,这样才能可靠的终止 TCP 连接。
b. 保证让迟来的 TCP 报文有足够的时间被识别并丢弃。
在 Linux 系统上,一个 TCP 端口不能被同时打开多次,当一个 TCP 连接处于 TIME_WAIT 状态时,我们无法使用该链接的端口来建立一个新连接。反过来思考,如果不存在 TIME_WAIT 状态,则应用程序能立即建立一个和刚关闭的连接相似的连接(这里的相似,是指他们具有相同的 IP 地址和端口号)。这个新的连接被称为原来连接的化身。新的化身可能收到属于原来连接携带应用程序数据的 TCP 报文段(迟到的报文段),这显然是不该发生的。这是 TIME_WAIT 状态存在的第二个原因。