-
TCP
-
TCP 基本认识
-
TCP 服务端的基本流程
- 创建服务端 socket,bind 绑定端口,listen 监听端口
- 将服务端 socket 注册到 epoll
- epoll_wait 等待连接的到来,连接到来时,调用 accepet 获取已连接的 socket
- 将已连接的 socket 注册到 epoll
- eopll_wait 等待事件发生
- 对方连接关闭,我方调用 close
-
TCP 基本头格式
-
源端口
-
目标端口
-
序列号
- 建立连接时由计算机生成的随机数作为初始值,通过 SYN 包传给接收端主机,每发送一次,就累加一次该数据字节数的大小。用来解决网络包乱序问题。
-
确认应答号
- 下一次期望收到的数据的序列号,发送端收到这个确认应答后可以认为这个序号以前的数据都已经被正常接收。
-
首部长度
-
控制位
- ACK:该值为 1 时,确认应答字段变为有效
- RST:1 时表示 TCP 出现异常必须强制断开连接
- SYN:1 时表示希望建立连接
- FIN:1 表示希望断开连接
-
窗口大小
-
校验和(16 位)
-
紧急指针(16 位)
-
选项
-
数据
-
-
为什么要 TCP 格式
- IP 层是不可靠的,不保证
- TCP 是一个工作在传输层的可靠数据传输服务,确保网络包是无损坏、无间隔、非冗余和按序。
-
什么是 tcp
-
含义:面向连接、可靠、基于字节流的传输层协议
-
面向连接
- 一对一
-
可靠:一定能到达接收端
-
字节流
-
TCP 连接:用于保证可靠性和流量控制维护的某些状态信息,包括 Socket、序列号、窗口大小
- Socket:IP 地址和端口组成
- 序列号:用来解决乱序问题
- 窗口大小:用来流量控制
-
-
如何确定一个 TCP 连接:源地址+源端口+目标地址+目标端口
-
TCP 和 UDP 的区别
-
TCP 不提供复杂的控制机制,利用 IP提供面向无连接的通信服务。
-
头部只有八个字节。
- 源端口
- 目标端口
- 包长度
- 校验和
- 数据
-
连接
-
服务对象
-
可靠性
-
拥塞控制、流量控制
-
首部开销
-
传输方式:TCP 是流式传输,没有边界,保证顺序和可靠性。 UDP 是一个包一个包发送,有边界,可能丢包和乱序。
-
分片不同
- TCP:数据如果大于 MSS 大小,会在传输层进行分片,目标主机收到后,会在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的分片。
- UDP:大于 MTU,进行分片,目标主机收到后,在 IP 层组装,交给传输层。
-
-
-
TCP 连接建立
-
三次握手
- 一开始服务器和客户端处理 close 状态,服务器先主动监听某个端口,处于LISTEN 状态。
- 客户端会随机初始化序列号,将序列号置于 TCP 首部的序号字段中,同时把 SYN 置为 1,接着把第一给 SYN 报文给服务器,表示向服务器发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SEND 状态
- 服务端收到客户端的报文后,初始化自己的序列号,将此序列号填入 TCP 序列号字段,然后把客户端发送过来的序号+1 填入确认应答字段。把 SYN、ACK 置为 1,然后发送给客户端。状态为 SYN_RCVD
- 客户端收到服务器的报文后,把 ACK 置为 1,在确认应答字段填入服务端的序号+1,然后把报文发送给服务器。处于ESTABLISHED.
- 服务器收到报文后,也处于 ESTABLISHED
-
为什么是三次握手
- 避免历史连接:防止旧的重复连接初始化造成混乱。
- 同步双方初始序列号
- 避免资源浪费:没有三次握手,服务器不清楚客户端是否收到了自己回复的 ACK 报文,所以服务器每收到一个SYN 就只能先主动建立一个连接,会造成多个冗余连接,造成资源浪费。
-
为什么每次建立 TCP 连接时,初始化的序列号要求不一致
-
为了防止历史报文被下一个相同四元组连接接收
- 如果每次建立连接,客户端和服务器初始化序列号都一样,很容易出现历史报文被下一个相同四元组的连接接收的问题。
-
安全性,防止黑客伪造相同序列号。
-
-
初始序列号 ISN是如何随机产生的
- 随机数会基于时钟计时器递增,基本不可能随机成一样的初始化序列号
-
既然 IP层会分片,为什么 TCP 还需要 MSS
- MTU:一个网络包的最大长度,以太网中一般为 1500 个字节。
- MSS:除去 IP 和 TCP 头部后,一个网络包所能容纳的 TCP 数据的最大长度。
- 概要:当 IP 层超过 MTU 大小的数据,IP 就要分片,把数据分成若干片,每一个分片都小于 MTU,由目标主机的 IP 层来进行重新组装后,交给上一层 TCP 传输层。如果 IP 分片丢失,整个 IP 报文都需要重传,没有效率。 所以为了达到最佳的传输效能,TCP 协议在建立连接的时候通常要协商双方的 MSS 值,在 TCP 层发现数据超过 MSS,就先会进行分片。经过 TCP 分片之后,如果一个 TCP 分片丢失了,进行重发的也是以 MSS 为单位,不需要重传所有分片。
-
第一次握手丢失,会发生什么?
- 如果客户端发送了报文,服务器没响应。会根据操作系统的超时时间进行重传,每一次重传时间是上一次的两倍,如果重传次数超过tcp_syn_retries,那么继续等待一段时间后就会断开连接
-
第二次握手丢失,会发生什么?
- 如果服务器发送一个 SYN-ACK 的报文,客户端没响应那么会发生下面的事情: 1. 客户端重传 SYN 报文,数量为tcp_syn_retries 2. 服务器由于没有收到客户端的确认报文,会进行 SYN-ACK 的报文重传,最大重传次数由TCP_synack_retries确定
-
第三次握手丢失,会发生什么
- 第三次握手丢失,相当于客户端回给服务器的 SYN-ACK 报文丢失了,这个时候会出发服务器的重传机制,当服务器重传次数超过一定次数后,会断开连接。
-
-
重传机制
-
超时重传:在发送数据时,设置一个定时器,当超过指定时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据,也就是超时重传。
- 数据包丢失
- RTT:数据发送时刻到接收到确认时刻的差值
- 确认应答丢失
-
快速重传:三次同样的接收到三次同样的 ACK 就会触发重发机制。
-
SACK:选择性确认,需要在 TCP 头部【选项】字段里加一个 SACK 的东西,可以将已收到的数据的信息发送给发送方。
-
D-SACK
- 可以让发送方知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了
- 可以知道是不是发送方的数据包被网络延迟了
- 可以知道网络是不是把发送方的数据报复制了
-
-
-
TCP 连接断开
-
四次回收过程
- 客户端打算关闭连接,发送一个 TCP 首部 FIN 标志位为 1 的报文,客户端进入FIN_WAIT_1 状态
- 服务器收到该报文,向客户端发送 ACK 应答报文,接着进入 CLOSE_WAIT 状态
- 客户端收到服务器的 ACK 应答后,进入FIN_WAIT_2 状态
- 服务器处理完数据后,也向客户端发送 FIN 报文,服务器进入 LAST_ACK 状态
- 客户端收到服务器的 FIN 报文后,回一个 ACK 应答报文,进入 TIME_WAIT 状态
- 服务端收到了 ACK 应答报文后,进入 CLOSE 状态
- 客户端经过 2MSL 后,自动进入 CLOSE 状态。
-
为什么挥手需要四次
- 关闭连接时,客户端向服务器发送 FIN,表示客户端不再发送数据了,但是还能接受数据。
- 服务器收到客户端的 FIN 报文后,先回一个 ACK 报文,但是服务器可能还有数据要处理和发送,等服务器不再发送数据了,才发送 FIN 报文给客户端来表示同意关闭连接。
-
第一次挥手丢失,会发生什么?
- 客户端发送 FIN 报文,如果没有收到服务器的响应,会出发重传机制,最大重传次数为tcp_orphan_retries。 如果超过这个,就自动进入 close。
-
第二次挥手丢失,会发生什么?
- 服务器发送的报文客户端没收到,会触发客户端的重传,一定次数后自动关闭。 如果客户端收到了二次挥手的报文,进入 FIN_WAIT2 状态,如果时间超过了tcp_fin_timeout,那么会自动关闭。 如果主动关闭方使用 shutdown 函数关闭连接,指定了只关闭发送方向,而接收方向没关闭,那么还可以接收数据。可能会死等。
-
第三次挥手丢失,会发生什么?
- 如果服务器发送了 FIN 报文,但是没收到 ACK 确定报文后,会重发,当重发次数超过tcp_orphan_retrie 参数后,会自动断开连接。
-
第四次挥手丢失,会发生什么?
- 如果客户端发送了 ACK 确定报文,服务器没收到的话,就会开启重传。如果重传超过次数就自动断开。 客户端因为收到了服务器的三次挥手,会进入TIME_WAIT 状态,开启时长2MSL 的定时器,如果途中收到第三次挥手的报文后,重置定时器,超过2MSL 后,断开连接。
-
为什么 TIME_WAIT 等待时间是 2MSL
- MSL:报文最大生存时间。 2MSL:相当于至少允许报文丢失一次。
-
为什么需要 TIME_WAIT 状态
- 防止历史连接中的数据,被后面相同四元组的连接错误接收
- 保证被动关闭连接的一方,能被正确的关闭
-
TIME_WAIT 过多有什么危害
- 占用系统资源,比如文件描述符,内存资源,CPU 资源,线程资源等。
- 占用端口资源
-
优化 TIME_WAIT
- net.ipv4.tcp.tw.reuse 和tcp_timestamps(复用处于 TIME_WAIT 的 socket 为新的连接所用)
- net.ipv4.tcp_max_tw_buckets(系统中处于TIME_WAIT 的连接一但超过这个值,系统就会降后面的 TIME_WAIT 连接状态重置)
- SO_LINGER:跳过四次挥手
-
服务器出现大量TIME_WAIT 状态的原因有哪些
- HTTP 没有使用长连接
- HTTP 长连接超时
- HTTP 长连接的请求数量达到上限
-
服务器出现大量 CLOSE_WAIT 原因
- 子主题 1
-
-
滑动窗口
-
含义:流量控制技术,用来控制发送方的数据传输速率,以便接收方能够高效、稳定地接收数据。
-
发送方滑动窗口
- 已发送并收到 ACK 确认
- 已发送但未发送 ACK 确认
- 未发送但总大小在接收方处理范围内
- 未发送但总大小超过接收方处理范围
-
接收方窗口大小
- 已成功接收并确认的数据
- 未收到数据,但可以接收的数据
- 未收到数据也不可以接收
-
-
流量控制
-
含义:TCP 提供一种机制可以让发送方根据接收方的实际接收能力控制发送的数据量。
-
操作系统可以动态增加或减少缓存区的大小,所以 TCP 规定不允许同时减少缓存又收缩窗口,而是采用先收缩窗口,过段时间再减少缓存。
-
窗口关闭:会造成死锁
- 如果 TCP 连接一方收到对方的零窗口通知,就启动持续定时器。如果持续计时器超时,就会发送窗口探测报文。这样就可以打破死锁局面。
-
糊涂窗口综合征
-
现象:1. 接收方可以通告一个小窗口 2. 发送方可以发送小数据。 3. 循环往复,就变成了很小的接收以及很小的发送。
-
解决: 1. 接收方不通告小窗口给发送方 2. 发送方避免发送小数据
- 接收方策略:当窗口大小小于Math.min(MSS,缓存空间/2),向发送方通告窗口为 0,阻止了发送方再发数据过来
- 发送方策略: 1. 窗口大小 >=MSS 并且 数据大小 >= MSS 2. 收到之前发送数据的 ACK 回包。
-
-
-
拥塞控制
-
拥塞窗口:发送方维护的一个状态变量,根据网络拥塞程度动态变化
-
发送窗口 = Math.min(拥塞窗口,接收窗口)
-
策略
-
慢启动:当发送方每收到一个 ACK,拥塞窗口大小就会加 1 当 cwnd < ssthresh,使用慢启动算法,当cwnd > ssthresh,使用拥塞避免算法
-
拥塞避免
- 每当收到一个 ACK,cwnd 增加1/cwnd
-
拥塞发生
- 如果发生超时重传,就会使用拥塞发生算法。
- ssthresh 设为cwnd/2 cwnd 重置为 1
-
快速恢复
-
发生快速重传,会启动快速恢复算法
-
cwnd = cwnd / 2 ssthresh = cwnd
-
收到三个重复的 ACK,触发快速重传机制 1. cwnd 变为原来的一半 2. ssthresh = cwnd 如果后面重复收到 ack,cwnd+1。 如果收到新的 ACK,cwnd 设置为第一部的 ssthresh,恢复到拥塞避免状态。
-
在快速恢复算法过程中,为什么收到新的数据后,cwnd 设置回了 ssthresh?
-
- 收先 ssthresh = cwnd/2,然后cwnd = ssthresh+3,表示网络出现了阻塞,需要减少 cwnd 以避免,加 3 代表快速重传时已经确认收到了三个重复的数据报。 2. 继续重传丢失的数据包,如果再收到重复的 ACK,cwnd+1.+1 代表每个收到的重复 ACK。 3. 收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh,恢复过程结束。 首先,快速恢复是拥塞发生后慢启动的优化,其首要目的仍然是降低 cwnd 来减缓拥塞,所以必然会出现 cwnd 从大到小的改变。 cwnd 遇到 ACK+1 的过程是为了尽快将丢失的数据包发给目标,从而解决拥塞的根本问题,所以 cwnd 是增大的。
-
-
-
-
-