TCP三次握手四次挥手
TCP基本认识
TCP的头格式
序列号:在建立连接时由计算机生成随机数作为初始值,通过SYN包传给接收端。每发送一次数据就累加一次该数据字节数的大小。用来解决网络号乱序。(乱序问题:未按发送顺序到达接收端称为乱序)
确认应答号:指下一次期望收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包问题。
控制位:
- ACK:该位为
1
时,确认应答的字段变为有效,TCP规定除了最初建立链接时的SYN包之外(最初必须为0)该位必须设置为1
.(跟ACK号不一样)。这里的位表示开关。 - RST:该位为
1
时,表示TCP连接中出现异常必须强制断开连接 - SYN:该位为
1
时,表示希望建立连接,并在其序列号的字段进行序列号初始值的设定。 - FIN:该位为
1
时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换FIN位为1的TCP段。
为什么使用TCP协议?TCP工作在那一层
IP
层不可靠的,不保证网络包是否交付、是否按序交付以及数据是否完整。
为了保证网络数据包的可靠性,需要由传输层的TCP
协议来负责。
TCP是一个工作在传输层的可靠数据传输的服务,能确保接收端接收到的网络包是无损坏的、无间隔的、非冗余、按序的。
TCP概念
TCP是面向连接、可靠的、基于字节流的传输层通讯协议。
面向连接:一定是一对一
才能连接,不能像UDP协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
可靠的:无论的网络链路中出现了怎样的链路变化,TCP都可以保证一个报文一定能够到达接收端;
字节流:用户消息通过TCP协议传输时,消息可能会被操作系统分组
成多个的TCP报文,如果接收方的程序如果不知道消息的边界
,是无法读出一个有效的用户信息的。并且TCP报文是有序的
,当前一个
TCP报文没有收到的时候,即使它先收到了后面的TCP报文,那么也不能扔给应用层去处理,同时对重复
的TCP报文会自动丢弃。
TCP连接是什么
用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。
Socket:由IP地址和端口号组成(用于精准定位通信双方)
序列号:用来解决乱序问题
窗口大小:用来做流量控制(指传输的数据量)
将 Socket、序列号和窗口大小等状态信息组合起来,就构成了一个 TCP 连接。这个连接定义了通信双方的身份(通过 Socket)、数据的传输顺序和完整性(通过序列号)以及数据的传输速率(通过窗口大小)。
如何唯一确定一个TCP连接(TCP四元组)
- 源地址
- 源端口
- 目的地址
- 目的端口
源地址和目的地址的字段(32位)是在IP头部中,作用是通过IP协议发送报文给对方主机
源端口和目的端口的字段(16位)是在TCP头部中,作用是告诉TCP协议应该把报文发给哪个进程
TCP最大连接数:客户端的IP数X客户端的端口数(理论),还受以下因素:
- 文件描述符限制:每个TCP连接都是一个文件,如果文件描述符被占满了,会发生Too many open file。Linux对可打开的文件描述符有三方面限制:
- 系统级:当前系统可打开的最大数量
- 用户级:指定用户可打开的最大数量
- 进程级:单个进程可打开的最大数量
- 内存限制:每个TCP连接都要占用一定内存,操作系统的内存是有限的,如果内存资源被占满后,会发生OOM。
UDP和TCP的区别
UDP利用IP提供面向无连接的通信服务
目标和源端口:告诉UDP协议应该把报文发给哪个进程
包长度:保存了UDP首部的长度跟数据的长度之和
校验和:为了提供可靠的UDP首部和数据而设计,防止收到在网络传输中受损的UDP包。
区别:
1.连接
- TCP是面向连接的传输层协议,传输数据前先建立连接
- UDP不建立连接直接传数据
2.服务对象 - TCP是一对一的两点服务,即一条服务只有两个端点
- UDP支持一对一、一对多、多对多的交互通信
3.可靠性 - TCP是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
- UDP是尽最大努力交付,不保证可靠交付数据。(但是可以基于UDP实现可靠传输协议如QUIC)
4.拥塞控制、流量控制 - TCP有拥塞控制和流量控制机制,保证数据传输的安全性
- UDP即使网络拥堵也不影响UDP发送速率
5.首部开销 - TCP首部长度较长有一定开销,首部没有使用选项字段时是20字节,使用选择字段则会变长
- UDP首部只有8字节,并且固定不变,开销较小
6.传输方式 - TCP是流式传播,没有边界,但保证顺序和可靠
- UDP是一个包一个包的发送,是有边界的,但是会出现丢包和乱序
7.分片不同 - TCP的数据大小如果大于MSS大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装TCP数据包,如果中途丢失分片,只需要传输丢失的这个分片
- UDP的数据大小如果大于MTU大小,则会在IP层进行分片。目标主机接收后在IP层组装完数据再传给传输层。
UDP和TCP使用场景
TCP是面向连接,保证数据可靠性交付,因此经常用于:
- FTP文件传输
- HTTP/HTTPS
UDP面向无连接,随时发送数据且简单又高效,因此用于: - 包总量较少的通信
- 视频、音频等
- 广播通信
TCP有可变长的选项字段,而UDP头部长度则不会变化,无需多一个字段去记录UDP首部长度
UDP为什么有包长度:
TCP数据长度=IP总长度+IP首部长度-TCP首部长度(UDP也可以使用这个公式)
TCP连接建立
TCP三次握手
-
开始服务端客户端都是close。服务器主动监听某个端口,处于
LISTEN
状态 -
客户端会随机初始化序列号,并置于TCP首部的字段,设置SYN标志位为1,接着把SYN报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于
SYN-SENT
状态 -
服务端收到SYN报文后,服务端首先初始化自己的序号并填入TCP序号字段,确认应答号处填入客户端序列号+1,标记SYN和ACK位为1.将报文发送给客户端,此报文也不包含应用层数据,之后服务器处于
SYN-RCVD
状态。 -
客户端收到后回应最后一个应答报文,TCP首部ACK标志位为1(表示应答字段有效),确认应答号字段填入服务端序列号+1,最后把报文发送给服务端,这次报文可以鞋底啊客户到服务端的数据,之后客户端处于
ESTABLISTEN
状态 -
服务端收到报文后也进入
ESTABLISTEN
状态
从上面的过程中可以发现第三次握手可以携带数据,前两次握手是不可以携带数据的。
三次握手原因
- 可以阻止重复历史连接的初始化(主要原因)
- 同步双方初始序列号
- 避免资源浪费
避免历史连接
为了防止旧的重复连接初始化造成混乱
实例:在网络拥堵的情况下
- 一个旧的SYN报文比新的早到达服务端,服务端回一个SYN+ACK报文给客户端即90+1
- 客户端收到后期望指是101,于是回RST报文
- 服务端收到RST报文后,就会释放连接
- 后续最新的SYN抵达服务端后,客户端与服务端正常完成三次握手
假设服务端前后收到两次报文,在收到RST之前
当服务端第一次收到SYN报文,就会回复SYN+ACK给客户端,确认号为91
这时候再收到新SYN报文,就会回Challenge Ack报文给客户端,这个ack报文并不是确认收到新SYN报文,是上次的ack确认号,即91。客户端收到此ACK报文后,跟期望值确认号对比发现不同,就会回RST报文。
三次握手建立连接主要原因为了防止历史连接初始化连接。
如果客户端 第三次ACK报文丢失,此时客户端为可发送状态,而服务端处于 syn_received状态,但是还是不影响客户端像服务端发送数据,因为数据报文中也有ack标识位也有确认号,这个确认号就告诉服务端客户端收到了第二次握手。
同步双方初始序列号
TCP通信双方都必须维护一个序列号,序列号是可靠传输的一个关键因素:
- 接收方可以去除重复的数据
- 接收方可以根据数据包的序列号按序接收
- 可以标识发送出去的数据包中,哪些是已经被对方收到的(通过ACK报文中的序列号知道)
客户端第一次携带初始序列号的SYN报文的时候,服务端回一个ACK应答报文,表示客户端的SYN报文已被服务端成功接收,那当服务端发送初始序列号给客户端时,依然也要得到客户端的应答回应,一来一回确保双方的初始序列号能被可靠的同步。
避免资源浪费
如果两次握手,当客户端发现的SYN报文在网络中阻塞,没有接收到ACK报文就会重新发送一个SYN,由于没有第三次握手,服务端不清楚客户端是否收到了ACK报文,所以服务端只能先主动建立一个连接,如果网络阻塞客户端发送多个SYN报文,服务端就会建立多个冗余无效连接,造成不必要的资源浪费。
小结
三次握手能防止历史连接的建立、能减少双方不必要的资源 开销、能帮助双方同步初始化序列号序列号能够保证数据包不重复、不丢弃和按序传输。
初始化序列号不一样的原因
- 防止历史报文被下一个相同四元组的连接接收(造成数据错乱)
- 为了安全性,防止黑客伪造 的相同序列号的TCP报文被对方接受;
有了IP分片,TCP层MSS的原因
MTU:一个网络包的最大长度,以太网中一般为1500字节
MSS:除去IP和TCP头部后,一个网络包所能容纳的TCP数据的最大长度
如果整个TCP报文交给IP层进行分片,会出现的异常情况
IP收到一个大于MTU大小的数据,IP层进行分片,然后进行发送由目标主机的IP层来进行重新组装后交给TCP层。如果一个IP分片丢失整个IP报文的所有分片都得重传因为IP层没有超时重传机制是能由TCP来负责超时和重传。因此IP层进行分片传输没有效率。
因此为了达到罪加传输效能TCP协议在建立连接的时候通常要协商双方的MSS值,当TCP层发现数据超过MSS就会进行分片,那由它形成的IP包的长度也就不会大于MTU自然不需要IP进行分片。如果TCP分片丢失进行重发也是以MSS为单位,大大加大了重传的效率。
第一次握手丢失
当客户端超时重传3次SYN报文达到最大重传次数,等待一段时间(为上一次超时时间的2倍),如果还没收到服务端收到的SYN+ACK客户端就会断开连接。
第二次握手丢失
第二次握手包含SYN服务端发起建立TCP连接的报文以及ACK对第一次握手的确认报文。
1.客户端意味自己的SYN报文丢失触发超时重传机制
2.服务端收不到第三次握手,服务端也会触发超时重传机制,重传SYN-ACK报文
客户端超时重传一次后达到最大重传次数,等待一段为上次等待时间2倍的事件后,客户端断开连接
服务端超时重传两次SYN-ACK报文后,达到最大重传次数,于是再等待一段时间,没收到客户端的第三次握手服务端就会断开连接。
第三次握手丢失
客户端收到来自服务端的SYN-ACK报文之后,就会给服务端回一个ACK报文,也就是第三次握手。如果第三次握手丢失,服务端会触发超时重传发送SYN-ACK报文,直到第三次握手或达到最大重传次数。
ACK报文是不会有重传的,当ACK丢失了就由对方重传对应的报文。
SYN攻击
攻击者短时间伪造不同IP地址的SYN报文,服务端每接收一个SYN报文就进入SYN_RCVD状态,但服务端发送出去的ACK+SYN报文,无法得知未知IP主机的ACK应答,这样就会占满服务端的半连接队列,服务端就不能为正常用户服务。
半连接队列:SYN队列
全连接队列:accept队列
危害:当TCP半连接队列满了再收到SYN就会丢弃掉,导致客户端无法和服务端建立连接
避免方法:
- 调大netdev_max_backlog(队列接收数据包的最大数量)
- 增大TCP半连接队列
- 开启tcp_syncookies;:服务端在收到SYN请求发送SYN+ACK响应时,不分配特定的资源而是根据该SYN包计算出一个cookie值。如果第三次握手根据包头信息重新计算的cookie等于开始的cookie值相同,则被视为正常连接;
- 减少SYN-ACK重传次数;
TCP连接断开
TCP四次挥手
- 客户端关闭连接,此时发送FIN报文,进入FIN_WAIT_1状态
- 服务端收到该报文后发送ACK报文,服务端进入CLOSE_WAIT状态
- 客户端收到ACK后,进入FIN_WAIT_2状态
- 服务端处理完数据后,向客户端发送FIN报文,服务端进入LAST_ACK状态
- 客户端收到服务端FIN报文后,回一个ACK应答报文,之后进入TIME_WAIT状态
- 服务端收到ACK后,就进入CLOSE状态,至此服务端完成连接的关闭
- 客户端经过2MSL一段时间后,自动进入CLOSE状态,至此客户端也完成连接关闭
每个方向都需要一个FIN和ACK,因此被称为四次挥手
主动关闭连接的,才有TIME_WAIT状态
四次挥手的原因
- 关闭连接时,客户端发送FIN包,代表客户端不发送数据但能接收数据
- 服务端收到客户端的FIN报文,先回一个ACK报文,服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN报文给客户端表示关闭连接
特定情况下,四次挥手可以变为三次
第一次挥手丢失
进入FIN_WAIT_1触发超时重传机制,达到最大重传次数再等待一段时间若还未收到ACK报文则直接进入CLOSE状态
第二次挥手丢失
客户端超时重传FIN报文,达到最大重传次数后再等待2倍上次等待时间的时间,若还未收到直接CLOSE
如果客户端调用close()函数关闭连接在FIN_WAIT_2状态下默认时间为60s,如果没收到来自服务端的FIN报文,客户端连接就会直接关闭
如果主动关闭方调用shutdown函数关闭连接,指定了只关闭发送方向,而接收方向并没有关闭,那么意味着主动关闭方还是接收数据。此时如果主动关闭方未收到第三次挥手,就会处于FIN_WAIT_2状态
第三次挥手丢失
服务端回复ACK报文后,同时处于CLOSE_WAIT状态,即等待应用程序调用close()触发服务端发送FIN报文连接进入LAST_ACK状态。如果FIN报文丢失即服务端迟迟收不到ACK报文,就会触发超时重传机制
第四次挥手丢失
服务端超时重传FIN报文,达到最大重传次数等待一会就直接断开连接
客户端进入FIN_WAIT_2状态收到服务端FIN报文开启2MSL定时器,再次收到服务端的FIN报文重置2MLS定时器,服务端重传几次FIN报文,客户端就重置几次2MLS定时器。最后再等待2MLS时长后,客户端也断开连接。
为什么TIME_WAIT需要等待2MSL
MLS是报文最大生存时间,超时这个报文将被丢弃。TCP报文基于IP协议,IP头中有一个TTL字段,是IP数据报可以经过的最大路由数,每经过一个处理值路由值减1,减为0则被丢弃。 TTL的个数一般是64,MSL设置为30s,认为经过64个路由器的时间小于30s。
确保最后的ACK可靠到达对方
当主动关闭的一端发送最后一个ACK,如果丢失另一端会重传FIN报文。等待2MSL允许主动关闭方再次收到重传的FIN报文,并重新计时重发ACK报文。允许报文丢失一次
消除旧连接的残留报文
网络报文因为网络延迟到达,如果相同的五元组的新连接被快速重用,旧连接的延迟报文可能被误认为属于新连接导致数据错乱,等待2MSL可以保证旧连接中的数据报文在网络中失效,避免干扰新的连接。
TIME_WAIT状态存在原因
- 防止历史连接中的数据被相同四元组的连接错误接收
- 保证被动关闭连接的一方能够正确的关闭,等待足够的时间以确保最后的ACK能让被动关闭方接收,从而帮助其正常关闭。如果没有这个状态,客户端收到FIN报文就会关闭如果ACK报文丢失此时服务端再次发送FIN报文,客户端已经关闭状态收到重传FIN报文后就会回RST报文。服务端收到RST将其解释为一个错误,这不是一个优雅的终止方式。
RST的危害
TIME_WAIT状态过多的危害
- 占用系统资源,比如内存资源、CPU资源
- 占用端口资源,端口资源也很有限