记录 - 5.2
mysql
round函数: 四舍五入,保留小数。
TCP 延迟确认与Nagle算法
Nagle 算法 : 满足任一条件
条件一 : 要等到窗口大小 >=
MSS
并且数据大小 >=MSS
条件二:收到之前发送数据的
ack
回包
延迟确认:
当有响应数据要发送时,ACK 会随着响应数据一起立刻发送给对方
当没有响应数据要发送时,ACK 将会延迟一段时间,以等待是否有响应数据可以一起发送
如果在延迟等待发送 ACK 期间,对方的第二个数据报文又到达了,这时就会立刻发送 ACK
tcp_retries1 和tcp_retries2
两者都是在TCP 三次握手之后的场景
当重传次数超过tcp_retries1就会指示IP 层进行MTU 探测、刷新路由等过程,并不会断开TCP 连接,当重传次数超过 tcp_retries2 才会断开TCP 流。
tcp_retries1 和tcp_retries2 两个重传次数都是受一个timeout 值限制的,当重传时间超过timeout,就不会重传了。
查看全连接队列
在listen状态时:
Recv-Q:当前全连接队列的大小,也就是当前已完成三次握手并等待服务端 accept() 的 TCP 连接;
Send-Q:当前全连接最大队列长度,上面的输出结果说明监听 8088 端口的 TCP 服务,最大全连接长度为 128
在非listen状态时:
Recv-Q:已收到但未被应用进程读取的字节数;
Send-Q:已发送但未收到确认的字节数;
当TCP 全连接队列满了会使用什么策略来回应客户端?
默认丢弃,也可以回复Rst复位报文。 通过 ipv4.tcp_abort_on_overflow
参数控制。0: 丢弃。 1:发送RST
如何增大TCP 全连接队列
TCP 全连接队列的最大值取决于 somaxconn 和 backlog 之间的最小值,也就是 min(somaxconn, backlog)
半连接队列的大小
半连接队列的最大值是 max_qlen_log 变量,半连接队列最大值不是单单由 max_syn_backlog 决定,还跟 somaxconn 和 backlog 有关系。

三次握手性能的提升
SYN_SENT、SYN_REV:
可以调整重传次数。如在内网中通讯时,适当调低重传次数,尽快把错误暴露给应用程序。

四次挥手的性能提升
关闭的方式:RST 报文和FIN 报文
如果进程收到RST 报文,直接关闭连接,这是一个暴力关闭连接的方式
close函数:关闭双向。
shutdown函数:优雅关闭,可以只关闭一个方向
FIN_WAIT1 状态的优化
重发由 tcp_orphan_retries
参数控制:orphan 虽然是孤儿的意思,该参数却不只对孤儿连接有效,事实上,它对所有 FIN_WAIT1 状态下的连接都有效,默认值是 0。 0,特指 8 次。
对于普遍正常情况时,调低 tcp_orphan_retries 就已经可以了。如果遇到恶意攻击,FIN 报文根本无法发送出去,这由 TCP 两个特性导致的:
首先,TCP 必须保证报文是有序发送的,FIN 报文也不例外,当发送缓冲区还有数据没有发送时,FIN 报文也不能提前发送。
其次,TCP 有流量控制功能,当接收方接收窗口为 0 时,发送方就不能再发送数据。所以,当攻击者下载大文件时,就可以通过接收窗口设为 0 ,这就会使得 FIN 报文都无法发送出去,那么连接会一直处于 FIN_WAIT1 状态。
解决方法:调整 tcp_max_orphans 参数,它定义了「孤儿连接」的最大数量。超出数量的将会直接发送RST报文强制关闭。
FIN_WAIT2 状态的优化
close关闭的连接,不会在FIN_WAIT2持续太久,tcp_fin_timeout 控制此时长,默认是60s,这与TIME_WAIT的时间是一样的。
但shutdown可以在这个状态持续,因为可能还可以发送或接收数据。
TIME_WAIT 状态的优化
当TIME_WAIT 连接超过一定量(tcp_max_tw_buckets)时,新关闭的连接就不再经历TIME_WAIT 而直接关闭。
连接复用。打开tcp_tw_reuse 参数和打开时间戳功能。此参数只适用于连接发起方。
如何连接双方同时关闭连接,会怎么样?

双方在等待 ACK 报文的过程中,都等来了 FIN 报文。这是一种新情况,所以连接会进入一种叫做 CLOSING 的新状态,它替代了 FIN_WAIT2 状态。接着,双方内核回复 ACK 确认对方发送通道的关闭后,进入 TIME_WAIT 状态,等待 2MSL 的时间后,连接自动关闭。
开启时间戳的好处
2MSL的问题不存在在了,因为重复的数据包会因为时间戳过期被自然丢弃
可以防止序列号绕回
便于精确计算RTT
传输性能的提升
如何确定最大的传输速度?
带宽时延积 BDP = RTT * 带宽
如果飞行报文超过了1MB,就会导致网络过载容易丢包。
怎样调整缓冲区大小
Linux 会根据设置的参数进行动态调节。
发送缓冲区是自行调节的,接收缓冲区可以根据系统空闲内存的大小来调节接收窗口。
发送缓冲区的调节功能是自动发开启的,而接收缓冲区则需要手动来开启调节功能。
小结

TCP 可靠性是通过 ACK 确认报文实现的,又依赖滑动窗口提升了发送速度也兼顾了接收方的处理能力。
内核缓冲区决定了滑动窗口的上限,缓冲区可分为:发送缓冲区 tcp_wmem 和接收缓冲区 tcp_rmem。
Linux 会对缓冲区动态调节,我们应该把缓冲区的上限设置为带宽时延积。发送缓冲区的调节功能是自动打开的,而接收缓冲区需要把 tcp_moderate_rcvbuf 设置为 1 来开启。其中,调节的依据是 TCP 内存范围 tcp_mem。
如何理解TCP 是面向字节流的协议
UDP: 操作系统不会对消息进行拆分,每个报文就是一个用户消息的边界。
TCP:不能认为一个用户消息对应一个TCP 报文,因此,TCP是面向字节流的协议。
为什么TCP 每次建立连接时,初始化序列号都要不一样呢?
防止历史数据被下一个相同的四元组错误的接收。
四次挥手的TIME_WAIT 不是会持续2MSL时长,历史报文不是早就在网络中消失了吗?
并不是所有连接都会通过四次挥手来正常关闭连接。
序列号回绕问题:
开启TCP时间戳。防回绕算法:要求双方维护最近一次收到数据包的时间戳,每收到一个新数据包都会跟上一个时间戳比较,如果发现时间戳不是递增的,则该数据包是过期的,直接丢弃该数据包。
如果时间戳也回绕了怎么办?
Linux 在 PAWS 检查做了一个特殊处理,如果一个 TCP 连接连续 24 天不收发数据则在接收第一个包时基于时间戳的 PAWS 会失效,也就是可以 PAWS 函数会放过这个特殊的情况,认为是合法的,可以接收该数据包。
SYN 报文什么情况下会被丢弃
开启 tcp_tw_recycle 参数,并且在 NAT 环境下,造成 SYN 报文被丢弃
TCP 两个队列满了(半连接队列和全连接队列),造成 SYN 报文被丢弃
已建立连接的TCP ,收到SYN会发生什么
客户端的SYN 报文里的端口号与历史连接的不相同:
服务端会认为是新的连接要建立,于是就会通过三次握手来建立新的连接。
旧连接处于 Established 状态的服务端最后会怎样呢?
如果服务端发送了数据包给客户端,由于客户端的连接已经被关闭了,此时客户的内核就会回 RST 报文,服务端收到后就会释放连接。
如果服务端一直没有发送数据包给客户端,在超过一段时间后,TCP 保活机制就会启动,检测到客户端没有存活后,接着服务端就会释放掉该连接。
客户端的 SYN 报文里的端口号与历史连接相同
处于Established 状态的服务端,如果收到了客户端的SYN 报文(SYN报文的初始化序列号是一个随机数,对于服务端就是一个乱序的序列号),会回复一个携带了正确序列号和确认号的ACK 报文,这个ACK 又称为 Challenage ACK ;
接着,客户端收到这个Challenage ACK ,发现确认号(ack num)并不是自己期望收到的,于是会回复RST 报文,服务端收到后,就会释放该连接。
如何关闭一个TCP 连接
杀掉进程。
killcx 工具
它会主动发送 SYN 包获取 SEQ/ACK 号,然后利用 SEQ/ACK 号伪造两个 RST 报文分别发给客户端和服务端,这样双方的 TCP 连接都会被释放,这种方式活跃和非活跃的 TCP 连接都可以杀掉。
tcpkill 工具
在双方进行 TCP 通信时,拿到对方下一次期望收到的序列号,然后将序列号填充到伪造的 RST 报文,并将其发送给对方,达到关闭 TCP 连接的效果。
总结
tcpkill 工具只能用来关闭活跃的 TCP 连接,无法关闭非活跃的 TCP 连接,因为 tcpkill 工具是等双方进行 TCP 通信后,才去获取正确的序列号,如果这条 TCP 连接一直没有任何数据传输,则就永远获取不到正确的序列号。
killcx 工具可以用来关闭活跃和非活跃的 TCP 连接,因为 killcx 工具是主动发送 SYN 报文,这时对方就会回复 Challenge ACK ,然后 killcx 工具就能从这个 ACK 获取到正确的序列号。
在 FIN_WAIT_2 状态下,是如何处理收到的乱序到 FIN 报文,然后TCP连接又是什么时候才进入到TIME_WAIT 状态?
在FIN_WAIT_2状态时,如果收到乱序的FIN 报文,那么就会加入到乱序队列
,并不会进入到TIME_WAIT 状态。
等收到前面被网络延迟的数据包时,会判断乱序队列有没有数据,然后会检测乱序队列中是否有可用的数据,如果能在乱序队列中找到与当前报文的序列号保持顺序的报文,就会看该报文是否有FIN 标志,如果发现有FIN 标志,这时才进入TIME_WAIT 状态。
在TIME_WAIT 状态的TCP 连接,收到SYN 后会发生什么?
合法 SYN:
客户端的 SYN 的「序列号」比服务端「期望下一个收到的序列号」要大,并且 SYN 的「时间戳」比服务端「最后收到的报文的时间戳」要大。
结果:重用此四元组连接,跳过2MSL 而转变为SYN_RECV状态。
非法 SYN:
客户端的 SYN 的「序列号」比服务端「期望下一个收到的序列号」要小,或者 SYN 的「时间戳」比服务端「最后收到的报文的时间戳」要小。
结果:会再回复一个第四次挥手的 ACK 报文,客户端收到后,发现并不是自己期望收到确认号(ack num),就回 RST 报文给服务端。
在 TIME_WAIT 状态,收到 RST 会断开连接吗?
如果 net.ipv4.tcp_rfc1337 参数为 0,则提前结束 TIME_WAIT 状态,释放连接。
如果 net.ipv4.tcp_rfc1337 参数为 1,则会丢掉该 RST 报文。
进程崩溃和主机宕机
客户端主机宕机,又迅速重启
客户端没重启完:不会响应报文;重启完后,收到之前的TCP报文,都会回复RST报文。是否有进程绑定该TCP目标端口号,TCP维护的结构信息都已经丢失了,只能重新建立连接。
如果「客户端进程崩溃」,客户端的进程在发生崩溃的时候,内核会发送 FIN 报文,与服务端进行四次挥手。
但是,「客户端主机宕机」,那么是不会发生四次挥手的,具体后续会发生什么?还要看服务端会不会发送数据?
如果服务端会发送数据,由于客户端已经不存在,收不到数据报文的响应报文,服务端的数据报文会超时重传,当重传总间隔时长达到一定阈值(内核会根据 tcp_retries2 设置的值计算出一个阈值)后,会断开 TCP 连接;
如果服务端一直不会发送数据,再看服务端有没有开启 TCP keepalive 机制?
如果有开启,服务端在一段时间没有进行数据交互时,会触发 TCP keepalive 机制,探测对方是否存在,如果探测到对方已经消亡,则会断开自身的 TCP 连接;
如果没有开启,服务端的 TCP 连接会一直存在,并且一直保持在 ESTABLISHED 状态。
linux命令
netstat
-a
或--all
:显示侦听套接字和非侦听套接字。-t
或--tcp
:只显示tcp套接字。-u
或--udp
:只显示udp套接字。-l
或--listening
:只显示侦听套接字。-n
或--numeric
:显示数字地址,而不是尝试确定符号主机、端口或用户名。-p
或--program
:显示每个套接字所属的程序的PID和名称。-r
或--route
:显示内核路由表。-s
或--statistics
:显示每个协议的汇总统计信息。