TCP建立连接
什么是TCP连接
用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括 Socket、序列号和窗口大小称为连接。
Socket:由 IP 地址和端口号组成
序列号:用来解决乱序问题等
窗口大小:用来做流量控制
三次握手作用
- 建立连接
- 协商起始序列号
- 协商双方的接收缓冲区大小
三次握手的过程
- 第一次握手:客户端发送一个SYN到服务器请求建立连接,这个SYN包含一个序列号(用于在后续的数据传输中进行标记),客户端进入SYN_SENT状态,等待服务器响应。
- 第二次握手:服务器收到SYN包后,捎带应答一个SYN+ACK(确认应答序列号+1)给客户端(包含服务器自己的序列号),服务器进入SYN_RECV状态,等待服务器响应。
- 第三次握手:客户端收到SYN+ACK后,发送一个ACK确认应答。这个ACK包包含对服务器的应答,即客户端确认应答序列号+1。客户端发送后状态变为ESTABLISHED,服务端收到报文后也变为此状态。
为什么是三次握手?
能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。
奇数次握手,可以确保一般情况握手失败的连接成本嫁接在客户端上。验证全双工的最小次数。
不是两次?
连接失败的成本会嫁接到服务端,无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号。
不是四次?
三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。
不是一次?
服务端接收到客户端的 SYN 包就认为建立连接成功的话,这样会带来 SYN 洪水攻击问题。
accept的第二个参数backlog
backlog+1表示底层已经建立好连接队列的最大长度。当服务器一个连接建立完成(即进入ESTABLISHED状态),当服务端没有调用accept函数,该连接便会进入该队列等待。如果队列已满,服务端收到连接后进入SYN_RECV状态(半连接状态)放入半连接队列,服务器不会长时间维护此状态,一定时间会丢弃。
TCP断开连接
四次挥手过程
- 第一次挥手:客户端主动向服务端发送FIN报文,请求断开连接(客户端进入FIN_WAIT_1状态),表明客户端不会再向服务端发送数据(但可以接受服务端发来的数据)。
- 第二次挥手:服务端接收到FIN报文后,会发送ACK报文(进入CLOSE_WAIT状态),表明服务端收到了客户端的FIN。而服务端可能还有数据需要进行处理和发送,连接并没有真正关闭。(客户端收到ACK进入FIN_WAIT_2状态)
- 第三次挥手:服务端处理完数据,发送FIN报文给客户端。(服务端变为LAST_ACK)
- 第四次挥手:客户端收到FIN报文后(状态变为TIME_WAIT),向服务端发送ACK报文,表明确认连接关闭。(服务端收到ACK报文后,状态变为CLOSED)
- 主动关闭连接的一方进行第四次挥手后,需要维持一段TIME_WAIT状态一段时间(报文最大生存时间)然后进入CLOSE状态。
TIME_WAIT状态
主动断开连接的一方会进入TIME_WAIT状态,该状态时连接没有彻底断开,ip和port正在被使用,无法立即重启。
setsockopt设置套接字能被复用
为什么 TIME_WAIT 等待的时间是 2MSL?
MSL 是 Maximum Segment Lifetime,报文最大生存时间,即报文从一端到另一端所需时间的最大值。它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
让通信双方历史数据得以消散。让断开连接时4次挥手有较好的容错性(即如果最后一次ACK丢失,客户端在TIME_WAIT还可以重发)。
如果客户端(主动关闭放)最后一次 ACK 报文(第四次挥手)在网络中丢失了,那么根据 TCP 可靠性原则,服务端(被动关闭方)会重传 FIN 报文。假设客户端(主动关闭连接放)没有 TIME_WAIT 状态,而是在发完最后一次 ACK 报文就直接进入 CLOSED 状态。如果该 ACK 报文丢失了,服务端则重传 FIN 报文,而此时客户端已经进入关闭状态了,在收到服务端重传的 FIN 报文后,就会回复 RST 报文。
服务端收到这个 RST 并将其解释为一个错误(Connection reset by peer),这对于一个可靠的协议来说不是一个优雅的终止方式。
为了防止这种情况出现,客户端必须等待足够长的时间,确保服务端能够收到 ACK,如果服务端没有收到 ACK,那么就会触发 TCP 重传机制,服务端会重新发送一个 FIN,这样一去一来刚好两个 MSL 的时间。
流量控制
TCP流量控制基于接收方的实际接收能力。在数据传输过程中,接收方会根据其缓冲区的大小和当前的网络状况,通过发送确认信号(ACK)来通知发送方可以发送的数据量。发送方则根据接收方的确认信号来控制其数据发送速率,确保发送的数据量不会超过接收方的处理能力。
- 接收端将自己接收缓冲区剩余空间的大小放入 TCP 报头中的窗口大小字段,通过 ACK 报文通知发送端。
- 窗口大小字段越大,说明网络的吞吐量越高。
- 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端。发送端接受到这个窗口之后,就会减慢自己的发送速度。
- 如果接收端缓冲区满了,就会将窗口大小置为0。这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。还有一种策略就是,接收端的缓冲区剩余空间更新后,接收端会给发送端发送窗口更新通知来告知对方接收缓冲区剩余空间的大小。
- TCP 是全双工的,可以在两个方向上进行流量控制。 三次握手时,会通过 TCP报头中的窗口大小字段来告知对方自己的接收缓冲区剩余空间的大小。那么在正式进行网络通信时,就已经得知对方的接收能力了,可以根据对方的接收能力来发送数据。
- 16位窗口字段最大能表示的值是 65535,那么 TCP 窗口最大就是 65535 字节么?并不是,实际上,TCP 报头 40字节选项中还包含了一个窗口扩大因子 M,实际窗口大小是窗口字段的值左移 M 位,M 的取值范围是 0 到 14。
滑动窗口
TCP滑动窗口是一种动态窗口,它的大小可以在一定范围内进行调整。发送方和接收方都维护一个窗口,窗口的大小决定了在无需等待对方确认的情况下,可以连续发送数据包的最大数量。
工作原理
TCP滑动窗口以字节为单位,分为发送窗口和接收窗口。发送方在发送数据时,会根据接收方的窗口大小来确定发送的数据量。当发送方发送一个数据包后,它会等待接收方的确认(ACK)报文。如果接收方成功接收了数据包并返回了ACK报文,那么发送方的滑动窗口就会向右滑动一格,继续发送下一个数据包。如果发送方在一段时间内没有收到接收方的ACK报文,那么它会认为数据包丢失,并启动超时重传机制。
如上图当发送端收到ACK2001后就向右滑动,滑动窗口大小表示无需等待 ACK 应答而可以继续发送数据的数量的最大值。大小是动态变化的。
- 右不变,左移动,窗口变小:对方应用层没有从tcp接收缓冲区拿数据。
- 左右都移动,整体的范围扩大/变小
start=确认序号
end=确认序号+窗口大小
滑动窗口一直向右移动并不会造成越界问题。因为发送缓存区是被看成环形队列的,当滑动窗口向右移动时,如果超出了缓冲区的范围,那么就会进行模除运算,重新回到缓冲区的起始位置进行向右移动。
丢包问题
情况一.ack丢失
- 允许少量的ack丢失,因为确认序号表示序号之前的报文都已经被接收。例如:当3000-4000丢失,3000-4000的应答报文中确认序号仍然是3001,4000-5000的报文中也是3001。
情况二.数据丢失
当1001-2000的报文丢失,主机A收到三次相同的确认应答,那就会触发快重传机制。
为何还需要超时重传?
快重传有条件的(必须三次一样的确认应答)才能触发,如果报文是最后一个或者倒数第二个就不满足快重传条件。超时重传作为一个兜底策略,而快重传是为了提高效率。
拥塞控制
如果通信双方出现了大量的数据丢包(超时)问题,tcp会判断网络出问题了(网络拥塞)。此时不能立即对报文进行超时重传。tcp协议是多主机在网络出现拥塞时达成共识
解决方案
TCP引入慢启动机制,先发少量数据探探路,如果得到应答,再慢慢增大数据量。
引入拥塞窗口概念,初始值为1,每次收到一个ack,拥塞窗口+1,每次发送数据包时,将拥塞窗口和接收端反馈的窗口大小(前文认为的滑动窗口大小)作比较,取最小值最为实际发送的大小。
- 拥塞窗口:主机判断网络健康状态的指标,超过拥塞窗口大小,会引发网络拥塞。
- 指数增长:刚开始发送数据时,拥塞窗口大小被设置为 1,并且以指数形式进行增长。因为指数前期增长较慢,可以避免网络拥塞问题。而中后期时指数增长快,此时网络也恢复了,可以尽快恢复双方的通信效率。
- 线性增长:慢启动的阈值初始时被设置为对方窗口大小的最大值,上图中的慢启动阈值为 16。当拥塞窗口的大小增长到慢启动阈值时就不再以指数形式进行增长,而采用线性增长的方式。注意:线性增长阶段时,还未发生网络拥塞问题。
- 乘法减小:当拥塞窗口大小增长到 24 时,发生了网络拥塞问题,此时慢启动阈值就会变成当前拥塞窗口大小的一半,同时拥塞窗口大小变成一。然后拥塞窗口再次以指数的形式进行增长,周而复始。
应答
延迟应答
接收端在处理完多个数据包后再发送ACK应答,从而增大了滑动窗口的大小,提高了数据传输的效率。
- 假设接收端缓冲区为 1M,一次收到了 500K 的数据。如果立刻进行应答,那么返回的窗口大小就是 500K。
- 但实际上接收方处理数据的速度可能会很快,10ms 之内就可以把 500K 数据从缓冲区消费掉了。
- 在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来。
- 如果接收端稍微等一会再应答,比如等待 200ms 再应答,那么这个时候接收方返回的窗口大小就是 1M。
- 数量限制:每隔 N 个包就应答一次。
- 时间限制:超过最大延迟时间就应答一次(这个时间不会导致超时重传)
捎带应答
TCP 协议的捎带应答机制就是发送的同一个 TCP 数据包中即包含数据又包含 ACK 应答的一种机制。
小结
可靠性
- 检验和
- 序列号(按序到达+去重)
- 确认应答
- 超时重发
- 连接管理
- 流量控制
- 拥塞控制
提高性能
- 滑动窗口
- 快速重传
- 延迟应答
- 捎带应答
- 流量控制