# TCP ## 什么是 TCP ? TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。 ## TCP三次握手流程? ![](https://static.cyub.vip/images/202107/tcp_connect.png) 开始客户端和服务器都处于CLOSED状态,然后服务端开始监听某个端口,进入LISTEN状态 1. 第一次握手(SYN=1, seq=x),发送完毕后,客户端进入 SYN_SEND 状态 2. 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1), 发送完毕后,服务器端进入 SYN_RCVD 状态。 3. 第三次握手(ACK=1,ACKnum=y+1),发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手,即可以开始数据传输。 ## 当TCP连接一个不存在的端口时候,会不会有三次握手过程? 不会有。因为当服务器收到没有监听端口的连接请求时会返回RST包。 ## TCP四次挥手过程? 1. 第一次挥手(FIN=1,seq=u),发送完毕后,客户端进入FIN_WAIT_1 状态 2. 第二次挥手(ACK=1,ack=u+1,seq=v),发送完毕后,服务器端进入CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态 3. 第三次挥手(FIN=1,ACK1,seq=w,ack=u+1),发送完毕后,服务器端进入LAST_ACK 状态,等待来自客户端的最后一个ACK。 4. 第四次挥手(ACK=1,seq=u+1,ack=w+1),客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。 ## TCP挥手为什么需要四次? 数据传输是双向传输的,一方告诉对方数据传输完成,需要两次挥手:一次发送通知给对方说我已经传输完成,一次需要接收到对方确认收到通知。也可以这么说每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手。 ## TIME-WAIT 状态为什么需要等待 2MSL? MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。 主动发起关闭连接的一方,才会有 TIME-WAIT 状态。 - 1个 MSL 保证四次挥手中主动关闭方最后的 ACK 报文能最终到达对端 - 1个 MSL 保证对端没有收到 ACK 那么进行重传的 FIN 报文能够到达 ## TIME_WAIT 过多有什么危害? 如果服务器有处于 TIME-WAIT 状态的 TCP,则说明是由服务器方主动发起的断开请求。过多的 TIME-WAIT 状态主要的危害有两种: - 第一是内存资源占用; - 第二是对端口资源的占用,一个 TCP 连接至少消耗一个本地端口,导致无法创建链接 端口资源有限,一般可以开启的端口为 32768~61000,也可以通过如下参数设置指定 > net.ipv4.ip_local_port_range ## 如何优化 TIME_WAIT? 1. 复用处于 TIME_WAIT 的 socket 为新的连接所用 > net.ipv4.tcp_tw_reuse = 1 ## 如何唯一确定一个 TCP 连接呢? 1. 源地址 2. 源端口 3. 目的地址 4. 目的端口 源地址和目的地址的字段(32位)是在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。源端口和目的端口的字段(16位)是在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。 ## 有一个 IP 的服务器监听了一个端口,它的 TCP 的最大连接数是多少? 服务端最大并发 TCP 连接数远不能达到理论上限: - 首先主要是文件描述符限制,Socket 都是文件,所以首先要通过 ulimit 配置文件描述符的数目; - 另一个是内存限制,每个 TCP 连接都要占用一定内存,操作系统是有限的。 ## TCP 和 UDP 的区别有哪些? - **TCP面向连接**((如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。 - TCP要求安全性,**提供可靠的服务**,通过TCP连接传送的数据,不丢失、不重复、安全可靠。而UDP尽最大努力交付,即不保证可靠交付。 - TCP是点对点连接的,UDP一对一,一对多,多对多都可以 - TCP传输效率相对较低,而UDP传输效率高,它适用于对高速传输和实时性有较高的通信或广播通信。 - TCP适合用于网页,邮件等;UDP适合用于视频,语音广播等 - **TCP面向字节流,UDP面向报文** ## TCP 是如何保证可靠性的? 1. 连接的可靠性:TCP的连接是基于三次握手,而断开则是四次挥手。确保连接和断开的可靠性 2. 数据传输的可靠性和可控性:支持报文校验、报文确认应答、超时重传、流量控制(滑动窗口) ## 超时重传机制是什么样的? 超时重传指的是**在发送数据报文时,设定一个定时器,每间隔一段时间,没有收到对方的ACK确认应答报文,就会重发该报文**。超时重传强调的是客户端。 ## 快速重传机制是什么样的? 它基于接收端的反馈信息来引发重传。接收方通过发送三次重复的ACK确认引发客户端快速重传延迟或丢失的报文。 举例子,发送端发送了 1,2,3,4,5,6 份数据: - 第一份 Seq=1 先送到了,于是就 Ack 回 2; - 第二份 Seq=2 也送到了,假设也正常,于是ACK 回 3; - 第三份 Seq=3 由于网络等其他原因,没送到; - 第四份 Seq=4 也送到了,但是因为Seq3没收到。所以ACK回3; - 后面的 Seq=4,5的也送到了,但是ACK还是回复3,因为Seq=3没收到。 - 发送端连着收到三个重复冗余ACK=3的确认(实际上是4个,但是前面一个是正常的ACK,后面三个才是重复冗余的),便知道哪个报文段在传输过程中丢失了,**于是在定时器过期之前(发送方的超时重传机制)**,重传该报文段。 - 最后,接收到收到了 Seq3,此时因为 Seq=4,5,6都收到了,于是ACK回7. 但快速重传还可能会有个问题:ACK只向发送端告知最大的有序报文段,到底是哪个报文丢失了呢?并不确定!那到底该重传多少个包呢?是重传 Seq3 呢?还是重传 Seq3、Seq4、Seq5、Seq6 呢?因为发送端并不清楚这三个连续的 ACK3 是谁传回来的。 解决上面问题,有两个办法: 1. 带选择确认的重传(SACK) 2. D-SACK ## TCP 滑动窗口是怎么回事? TCP 发送一个数据,需要收到确认应答,才会发送下一个数据。这样有个缺点,就是效率会比较低。 为了解决这个问题,TCP引入了窗口,它是操作系统开辟的一个缓存空间。窗口大小值表示无需等待确认应答,而可以继续发送数据的最大值。 TCP头部有个字段叫win,也即那个16位的窗口大小,它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度,从而达到流量控制的目的。 TCP 滑动窗口分为两种: 发送窗口和接收窗口。发送端的滑动窗口包含四大部分,如下: - 已发送且已收到ACK确认 - 已发送但未收到ACK确认 - 未发送但可以发送 - 未发送也不可以发送 ![](https://static.cyub.vip/images/202107/tcp_send_win.webp) 接收方的滑动窗口包含三大部分,如下: - 已成功接收并确认 - 未收到数据但可以接收 - 未收到数据并不可以接收的数据 ![](https://static.cyub.vip/images/202107/tcp_recv_win.webp) ## TCP的流量控制是怎么回事? TCP 提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量,这就是流量控制。流量控制是作用于接收者的,根据接收端的实际接收能力控制发送速度,防止分组丢失的。 注意TCP的拥塞控制作用于网络的,防止过多的数据包注入到网络中,避免出现网络负载过大的情况。 ## TCP的粘包和拆包是如何实现的? TCP是面向流,没有界限的一串数据。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。 **为什么会产生粘包和拆包呢?** - 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包; - 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包; - 要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包; - 待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。即TCP报文长度-TCP头部长度>MSS。 **解决方案** - 在数据尾部增加特殊字符进行分割 - 将数据分为两部分,一部分是头部,一部分是内容体;其中头部结构大小固定,且有一个字段声明内容体的大小 ## 半连接队列和 SYN Flood 攻击的关系? 一个完整的连接建立过程,服务器会经历 2 种 TCP 状态:SYN_REVD, ESTABELLISHED。对应也会维护两个队列: - 一个存放SYN的队列(半连接队列,也成SYN队列) - 一个存放已经完成连接的队列(全连接队列, 也称Accept队列) ![](http://static.cyub.vip/images/202001/tcp_sync_queue.jpg) SYN Flood是一种典型的DoS (Denial of Service,拒绝服务) 攻击,它在短时间内,伪造不存在的IP地址,向服务器大量发起SYN报文。当服务器回复SYN+ACK报文后,不会收到ACK回应报文,导致服务器上建立大量的半连接半连接队列满了,这就无法处理正常的TCP请求。 解决办法是: - syn cookie:在收到SYN包后,服务器根据一定的方法,以数据包的源地址、端口等信息为参数计算出一个cookie值作为自己的SYN ACK包的序列号,回复SYN+ACK后,服务器并不立即分配资源进行处理,等收到发送方的ACK包后,重新根据数据包的源地址、端口计算该包中的确认序列号是否正确,如果正确则建立连接,否则丢弃该包。 ## 以太网下,TCP包最大负载是多少? 最大负载大小(MSS) = MTU(1500) - IP头大小(20) - TCP头大小(20) = 1460 IP和TCP头大小不固定,最小是20字节