传输层的特性以及主要功能(一次关于传输层基本原理及故障分析)
传输层的特性以及主要功能(一次关于传输层基本原理及故障分析)服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:半连接队列,也称 SYN 队列;全连接队列,也称 accepet 队列;TCP流量控制TCP半连接与全连接TCP 半连接队列和全连接队列满了会发生什么?又该如何应对?
导语传输层最重要的是 TCP 和 UDP 协议,所以这儿整理的主要是对这两种协议的原理和常见故障分析。
正文1、基本原理
TCP 提供了面向连接的可靠传输服务。要优化 TCP,我们首先要掌握 TCP 协议的基本原理,比如流量控制、慢启动、拥塞避免、延迟确认以及状态流图(如下图所示)等。掌握这些原理后,就可以在不破坏 tcp 正常工作的基础上,对它进行优化。
UDP 不提供复杂的控制机制,利用 IP 提供面向「无连接」的通信服务。
连接建立与断开
TCP状态转移
TCP流量控制
TCP半连接与全连接
TCP 半连接队列和全连接队列满了会发生什么?又该如何应对?
在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:
半连接队列,也称 SYN 队列;
全连接队列,也称 accepet 队列;
服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。
不管是半连接队列还是全连接队列,都有最大长度限制,超过限制时,内核会直接丢弃,或返回 RST 包。
在「LISTEN 状态」时,Recv-Q/Send-Q 表示的含义如下:
Recv-Q:当前全连接队列的大小,也就是当前已完成三次握手并等待服务端 accept() 的 TCP 连接个数;TCP 全连接队列最大值取决于 somaxconn 和 backlog 之间的最小值,也就是 min(somaxconn backlog)。somaxconn 是 Linux 内核的参数,默认值是 128,可以通过 /proc/sys/net/core/somaxconn 来设置其值;backlog 是 listen(int sockfd int backlog) 函数中的 backlog 大小,nginx 默认值是 511,可以通过修改配置文件设置其长度;
Send-Q:当前全连接最大队列长度,上面的输出结果说明监听 8088 端口的 TCP 服务进程,最大全连接长度为 128;
在「非 LISTEN 状态」时,Recv-Q/Send-Q 表示的含义如下:
Recv-Q:已收到但未被应用进程读取的字节数;
Send-Q:已发送但未收到确认的字节数;
查看当前半连接队列大小(半连接大小由内核参数somaxconn及tcp_max_syn_backlog决定)
netstat -natp | grep SYN_RECV | wc -l
查看当前是否存在全队列溢出的情况(间隔数秒后再次运行,看数值是否增多)
netstat -s | grep overflowed
查看当前是否存在半连接队列溢出的情况(间隔数秒后再次运行,看数值是否增多)
netstat -s | grep "SYNs to LISTEN"
2、网络性能测试
网络性能评估主要是监测网络带宽的使用率,将网络带宽利用最大化是保证网络性能的基础,但是由于网络设计不合理、网络存在安全漏洞等原因,都会导致网络带宽利用率低。要找到网络带宽利用率低的原因,可以对网络传输进行监控,此时就需要用到一些网络性能评估工具,而iperf就是这样一个网络带宽测试工具。
iperf 和 netperf 都是最常用的网络性能测试工具,测试 TCP 和 UDP 的吞吐量。它们都以客户端和服务器通信的方式,测试一段时间内的平均吞吐量。
iperf是一个基于 TCP/IP 和 UDP/IP 的网络性能测试工具,它可以用来测量网络带宽和网络质量,还可以提供网络延迟抖动、数据包丢失率、最大传输单元等统计信息。网络管理员可以根据这些信息了解并判断网络性能问题,从而定位网络瓶颈,解决网络故障。
本机带宽可以用 ethtool 来查询,它的单位通常是 Gb/s 或者 Mb/s,不过注意这里小写字母 b ,表示比特而不是字节。我们通常提到的千兆网卡、万兆网卡等,单位也都是比特。如下,eth0 网卡就是一个千兆网卡:
$ ethtool eth0 | grep Speed
Speed: 1000Mb/s
centos系统 iperf安装
方法一:yum方式
目前,iperf 的最新版本为 iperf3,可以运行下面的命令来安装:
yum install iperf3
方法二:安装包方式
iperf支持Win32、Linux、FreeBSD、Mac OS X、OpenBSD和Solaris等多种操作系统平台。可以从iperf官方网站:http://iperf.fr/下载各种版本,这里下载的软件包为iperf-3.0.tar.gz,安装过程如下:
$ tar zxvf iperf-3.0.tar.gz
$ cd iperf
$ make
$ make install
iperf使用
服务器端专用选项的含义如下表所示
客户端专用选项的含义如下表所示
客户端与服务器端公用选项的含义如下表所示图片
TCP性能测试(吞吐)
iperf测试TCP带宽的原理比较简单,在客户端和服务端建立三次握手连接后,客户端带宽的大小等于发送的总数据除以发送的总时间。对服务端测得的带宽,则是接收的总数据除以所花时间。
步骤一:在目标机器上启动 iperf 服务端:
步骤二:在另一台机器上运行 iperf 客户端,运行测试:
注意禁止使用同一台机器进行测试,否则结果不准。
步骤三:查看结果报告
客户端处的输出
可以看到每15秒传输的数据量在995MB左右,刚好与“Bandwidth”列的值对应起来,网卡的带宽速率维持在995Mbits/sec左右,而测试的服务器是千兆网卡,这个测试值也基本合理。在输出的最后,iperf还给出了总的数据发送、接收量,以及带宽速率平均值,通过这些值,基本可以判断网络带宽是否正常,网络传输状态是否稳定。
iperf也可以用于测量UDP数据包的带宽、抖动和丢包率。但是,由于UDP协议是一个面向非连接的轻量级传输协议,不提供可靠的数据传输服务。因此,一般不对UDP进行带宽测试。
iperf测试UDP性能时,客户端可以指定UDP数据流的速率。客户端发送数据时,将根据客户端提供的速率计算数据包发送之间的时延。客户端还可以指定发送数据包的大小。每个发送的数据包都有一个ID号,服务器端根据该ID号来确定数据报丢失和乱序。
数据报传输延迟抖动 (Jitter)的测试由服务器端完成,客户发送的报文数据包含有发送时间戳,服务器端根据该时间信息和接收到报文的时间戳来计算传输延迟抖动。传输延迟抖动反映传输过程中是否平滑。由于它是一个相对值,所以并不需要客户端和服务器端时间同步。
目标端的服务器命令不变,客户端命令只需再加一个-u即可
主要看服务器端的结果,截图如下:
客户度结果截图如下:
“Jitter”列表示抖动时间,或者称为传输延迟,“Lost/Total”列表示丢失的数据包和总的数据包数量,后面的55%是平均丢包的比率,“Datagrams”列显示的是总共传输数据包的数量。在这个输出中,详细记录了在传输过程中,每个阶段的传输延时和丢包率,在UDP应用中随着传输数据的增大,丢包率和延时也随之增加。对于延时和丢包可以通过改变应用程序来缓解或修复,例如视频流应用,通过缓存数据的方式可以容忍更大的延时。
「网络延迟」即网络数据传输所用的时间。注意,这个时间可能是单向的,指从源地址发送到目的地址的单程时间;也可能是双向的,即从源地址发送到目的地址,然后又从目的地址发回响应,这个往返全程所用的时间。
另一个指标「应用程序延迟」,是指从应用程序接收到请求,再到发回响应,全程所用的时间。通常,应用程序延迟也指的是往返延迟,是网络数据传输时间加上数据处理时间的和。
网络延迟测试工具
最简单的,使用 ping 来测试网络延迟。
ping 基于 ICMP 协议,它通过计算 ICMP 回显响应报文与 ICMP 回显请求报文的时间差,来获得往返延时。这个过程并不需要特殊认证,常被很多网络攻击利用,比如端口扫描工具 nmap、组包工具 hping3 等等。所以,为了避免这些问题,很多网络服务会把 ICMP 禁止掉,这也就导致我们无法用 ping ,来测试网络服务的可用性和往返延时。
这时,我们可以用 traceroute 或 hping3 的 TCP 和 UDP 模式(必须指定端口),来获取网络延迟。比如,以 baidu.com 为例,可以执行下面的 hping3 命令,测试你的机器到百度搜索服务器的网络延迟:
traceroute 会在路由的每一跳发送三个包,并在收到响应后,输出往返延时。如果无响应或者响应超时(默认 5s),就会输出一个星号。
分析示例
选取《Linux性能优化实践》中的一个例子。服务端为修改配置后的nginx服务器(服务端口号为8080),客户端为wrk命令行「wrk --latency -c 100 -t 2 --timeout 2 http://172.22.31.228:8080/」发起的性能测试。
服务端通过tcpdump进行抓包
# -i指定网卡,一定要指定正确的网卡,否则抓不到任何包
$ tcpdump -nn tcp port 8080 -i lo -w nginx.pcap
对于多并发请求,我们可以选中关心的tcp报文中的一个,点击 右键--> Follow-> Tcp Stream 即可,等同于 tcp.stream eq 1 这样来过滤,如下图所示: tcp.stream可理解为握手后一对传输之间的通讯被赋予的一个号码。可以标识一次完整的tcp通讯,包括建立、通讯和释放。
过滤后,观察这一次完整的通信过程:
通过这个图就可以看出,前面三次握手,以及第一次 HTTP 请求和响应还是挺快的,但第二次 HTTP 请求就比较慢了,特别是客户端在收到服务器第一个分组后,40ms 后才发出了 ACK 响应(图中红色框)。
再仔细观察 Wireshark 的界面,其中, 1108 号包,就是刚才说到的延迟 ACK 包;下一行的 1110 ,则是 Nginx 发送的第二个分组包,它跟 653 号包组合起来,构成一个完整的 HTTP 响应(ACK 号都是 89)。
第二个分组没跟前一个分组(653 号)一起发送,而是等到客户端对第一个分组的 ACK 后(1108 号)才发送,这看起来跟延迟确认有点像,只不过,这儿不再是 ACK,而是发送数据。
针对 TCP ACK 的一种优化机制,即不用每次请求都发送一个 ACK,而是先等一会儿(比如 40ms),看看有没有“顺风车”。如果这段时间内,正好有其他包需要发送,那就捎带着 ACK 一起发送过去。当然,如果一直等不到其他包,那就超时后(40ms)单独发送 ACK。
查询 TCP 文档(执行 man tcp),只有 TCP 套接字专门设置了 TCP_QUICKACK ,才会开启快速确认模式;否则,默认情况下,采用的就是延迟确认机制。
而通过「strace -f 命令」 查看客户端的系统调用,并没有设置TCP_QUICKACK,只设置了TCP_NODELAY(表示禁用Nagle算法),这里说明客户端采用了延迟确认机制。
$ strace -f wrk --latency -c 100 -t 2 --timeout 2 http://172.22.31.228:8080/
...
setsockopt(52 SOL_TCP TCP_NODELAY [1] 4) = 0
...
Nagle 算法(纳格算法)
Nagle 算法,是 TCP 协议中用于减少小包发送数量的一种优化算法,目的是为了提高实际带宽的利用率。
举个例子,当有效负载只有 1 字节时,再加上 TCP 头部和 IP 头部分别占用的 20 字节,整个网络包就是 41 字节,这样实际带宽的利用率只有 2.4%(1/41)。往大了说,如果整个网络带宽都被这种小包占满,那整个网络的有效利用率就太低了。
Nagle 算法正是为了解决这个问题。它通过合并 TCP 小包,提高网络带宽的利用率。Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。在收到这个分组的 ACK 前,不发送其他分组。这些小分组会被组合起来,并在收到 ACK 后,用同一个分组发送出去。
显然,Nagle 算法本身的想法还是挺好的,但是碰上 Linux 默认的延迟确认机制后,就不是这么好了。因为它们一起使用时,网络延迟会明显。如下图所示:
- 当 Sever 发送了第一个分组后,由于 Client 开启了延迟确认,就需要等待 40ms 后才会回复 ACK。
- 同时,由于 Server 端开启了 Nagle,而这时还没收到第一个分组的 ACK,Server 也会在这里一直等着。
- 直到 40ms 超时后,Client 才会回复 ACK,然后,Server 才会继续发送第二个分组。
因此将nginx服务器的配置禁止Nagle算法即可降低网络请求时延。
此时 Nginx 不用再等 ACK,536 和 540 两个分组是连续发送的;而客户端虽然仍开启了延迟确认,但这时收到了两个需要回复 ACK 的包,所以也不用等 40ms,可以直接合并回复 ACK。
启动TCP_NODELAY,就意味着禁用了Nagle算法,允许小包的发送。对于延时敏感型,同时数据传输量比较小的应用,开启TCP_NODELAY选项无疑是一个正确的选择。比如,对于SSH会话,用户在远程敲击键盘发出指令的速度相对于网络带宽能力来说,绝对不是在一个量级上的,所以数据传输非常少;而又要求用户的输入能够及时获得返回,有较低的延时。如果开启了Nagle算法,就很可能出现频繁的延时,导致用户体验极差。当然,你也可以选择在应用层进行buffer,比如使用java中的buffered stream,尽可能地将大包写入到内核的写缓存进行发送;vectored I/O(writev接口)也是个不错的选择。
对于关闭TCP_NODELAY,则是应用了Nagle算法。数据只有在写缓存中累积到一定量之后,才会被发送出去,这样明显提高了网络利用率(实际传输数据payload与协议头的比例大大提高)。但是这又不可避免地增加了延时;与TCP delayed ack这个特性结合,这个问题会更加显著,延时基本在40ms左右。当然这个问题只有在连续进行两次写操作的时候,才会暴露出来。
连续进行多次对小数据包的写操作,然后进行读操作,本身就不是一个好的网络编程模式,在应用层就应该进行优化。
对于既要求低延时,又有大量小数据传输,还同时想提高网络利用率的应用,大概只能用UDP自己在应用层来实现可靠性保证了。
在发现网络延迟增大后,可以用 traceroute、hping3、tcpdump、Wireshark、strace 等多种工具,来定位网络中的潜在问题。比如,
使用 hping3 以及 wrk 等工具,确认单次请求和并发请求情况的网络延迟是否正常。
使用 traceroute,确认路由是否正确,并查看路由中每一跳网关的延迟。
使用 tcpdump 和 Wireshark,确认网络包的收发是否正常。
使用 strace 等,观察应用程序对网络套接字的调用情况是否正常。
这样,你就可以依次从路由、网络包的收发、再到应用程序等,逐层排查,直到定位问题根源。
DoS(Denail of Service),即拒绝服务攻击,指利用大量的合理请求,来占用过多的目标资源,从而使目标服务无法响应正常请求。
DDoS(Distributed Denial of Service) 则是在 DoS 的基础上,采用了分布式架构,利用多台主机同时攻击目标主机。这样,即使目标服务部署了网络防御设备,面对大量网络请求时,还是无力应对。
从攻击的原理上来看,DDoS 可以分为下面几种类型。
第一种,耗尽带宽。无论是服务器还是路由器、交换机等网络设备,带宽都有固定的上限。带宽耗尽后,就会发生网络拥堵,从而无法传输其他正常的网络报文。
第二种,耗尽操作系统的资源。网络服务的正常运行,都需要一定的系统资源,像是 CPU、内存等物理资源,以及连接表等软件资源。一旦资源耗尽,系统就不能处理其他正常的网络连接。
第三种,消耗应用程序的运行资源。应用程序的运行,通常还需要跟其他的资源或系统交互。如果应用程序一直忙于处理无效请求,也会导致正常请求的处理变慢,甚至得不到响应。
比如,构造大量不同的域名来攻击 DNS 服务器,就会导致 DNS 服务器不停执行迭代查询,并更新缓存。这会极大地消耗 DNS 服务器的资源,使 DNS 的响应变慢。
无论是哪一种类型的 DDoS,危害都是巨大的。那么,如何可以发现系统遭受了 DDoS 攻击,又该如何应对这种攻击呢?
此处将用到hping3工具,它可以用来构造 TCP/IP 协议数据包,对系统进行安全审计、防火墙测试、DoS 攻击测试等。
DoS攻击的问题是,当我们发送所有这些数据包的服务器,它有我们的地址在里面。所有的管理员要做的就是查看日志,我们的地址会很快的被查找出来,然后就GG了。我们不仅要发起SYN洪流,而且要欺骗我们的地址,攻击之前,让我们更深入地讨论SYN泛洪的概念。
SYN洪流会向服务器疯狂的发送请求数量,以便用尽所有的资源。你可能会好奇:“ SYN与使用资源有什么关系?“。那么这就是TCP三方握手。SYN代表syn chronize。当我们发送一个SYN数据包时,我们将建立一个连接。
我们可以看到攻击者向受害者发送了很多SYN数据包(带有欺骗地址)。受害者用SYN-ACK做出响应来确认连接,但由于没有响应,这样就会使得服务端有大量的处于 SYN_RECV 状态的 TCP 连接,这种等待状态的 TCP 连接,通常也称为半开连接。由于连接表的大小有限,大量的半开连接就会导致连接表迅速占满,从而无法建立新的 TCP 连接。
使用tcpdump命令抓包
tcpdump -i lo -n tcp port 8080
使用hping3进行syn泛洪攻击
# -p 端口号
# -i u100表示每隔100微秒发送一个网络帧
# -S 发送TCP的SYN包
# -A 发送TCP的ACK包
# -c参数可以指定收发数据包的个数
# -a 伪造源IP
# --flood 尽可能快的发送,慎用
$ hping3 -S -p 8080 -i u100 172.22.31.228 -a 1.2.3.4
放入wireshark查看抓包情况:
Flags [S] 表示这是一个 SYN 包。大量的 SYN 包表明,这是一个 SYN FLOOD 攻击。
查看当前 TCP 半连接队列大小:
SYN_RECVED状态的连接达到最大值256,且持续不变。之前一直误以为半连接队列的大小只由tcp_max_syn_backlog参数决定,而查看本机该参数的大小并不为256。其具体决定因素可查看文章TCP 半连接队列和全连接队列满了会发生什么?又该如何应对?
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
解决方案
为了缓解 SYN FLOOD 等,利用 TCP 协议特点进行攻击而引发的性能问题,可以考虑优化与 SYN 状态相关的内核选项,比如采取下面几种措施。
这里给出几种防御 SYN 攻击的方法:
方法一:增大半连接队列;
要想增大半连接队列,我们得知不能只单纯增大 tcp_max_syn_backlog 的值,还需一同增大 somaxconn 和 backlog,也就是增大全连接队列。否则,只单纯增大 tcp_max_syn_backlog 是无效的。
增大 tcp_max_syn_backlog 和 somaxconn 的方法是修改 Linux 内核参数:
echo 1024 > /proc/sys/net/ipv4/tcp_max_syn_backlog
echo 1024 > /proc/sys/net/core/somaxconn
增大 backlog 的方式,每个 Web 服务都不同。该参数即为 listen() 的第二个参数。
方法二:开启 tcp_syncookies 功能(即使半连接队列满,非异常请求也可以正常建立);
开启 syncookies 功能就可以在不使用 SYN 半连接队列的情况下成功建立连接,在前面我们源码分析也可以看到这点,当开启了 syncookies 功能就不会丢弃连接。
syncookies 是这么做的:服务器根据当前状态计算出一个值,放在己方发出的 SYN ACK 报文中发出,当客户端返回 ACK 报文时,取出该值验证,如果合法,就认为连接建立成功,如下图所示。
syncookies 参数主要有以下三个值:
0 值,表示关闭该功能;
1 值,表示仅当 SYN 半连接队列放不下时,再启用它;
2 值,表示无条件开启功能;
那么在应对 SYN 攻击时,只需要设置为 1 即可:
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
方法三:减少 SYN ACK 重传次数。
当服务端受到 SYN 攻击时,就会有大量处于 SYN_REVC 状态的 TCP 连接,处于这个状态的 TCP 会重传 SYN ACK ,当重传超过次数达到上限后,就会断开连接。
那么针对 SYN 攻击的场景,我们可以减少 SYN ACK 的重传次数,以加快处于 SYN_REVC 状态的 TCP 连接断开。
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
5、常见故障三:大量TIME_WAIT状态连接
Time_wait状态存在的意义:
经过 2MSL 这个时间(在 Linux 系统里 2MSL 默认是 60 秒),足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。
等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。
在请求数比较大的场景下,可能会看到大量处于 TIME_WAIT 状态的连接,过多的 TIME-WAIT 状态主要的危害有两种:
第一是内存资源占用;
第二是对端口资源的占用,一个 TCP 连接至少消耗一个本地端口;
第二个危害是会造成严重的后果的,要知道,端口资源也是有限的,一般可以开启的端口为 32768~61000,也可以通过参数「net.ipv4.ip_local_port_range」设置指定。
这时,我们可以优化与 TIME_WAIT 状态相关的内核选项,比如采取下面几种措施。
1、增大处于 TIME_WAIT 状态的连接数量 net.ipv4.tcp_max_tw_buckets ,并增大连接跟踪表的大小 net.netfilter.nf_conntrack_max(net.ipv4.tcp_max_tw_buckets值默认为 18000,当系统中处于 TIME_WAIT 的连接一旦超过这个值时,系统就会将所有的 TIME_WAIT 连接状态重置,这个方法过于暴力,而且治标不治本)
2、减小 net.ipv4.tcp_fin_timeout 和 net.netfilter.nf_conntrack_tcp_timeout_time_wait ,让系统尽快释放它们所占用的资源。
3、开启端口复用 net.ipv4.tcp_tw_reuse。这样,被 TIME_WAIT 状态占用的端口,还能用到新建的连接中。
4、增大本地端口的范围 net.ipv4.ip_local_port_range 。这样就可以支持更多连接,提高整体的并发能力。
5、增加最大文件描述符的数量。你可以使用 fs.nr_open 和 fs.file-max ,分别增大进程和系统的最大文件描述符数;或在应用程序的 systemd 配置文件中,配置 LimitNOFILE ,设置应用程序的最大文件描述符数。
TCP 有一个机制是保活机制。这个机制的原理是这样的:
定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。
在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为默认值:
tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制
tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;
tcp_keepalive_probes=9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。
也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接。
这个时间是有点长的,我们也可以根据实际的需求,对以上的保活相关的参数进行设置。
1、缩短最后一次数据包到 Keepalive 探测包的间隔时间 net.ipv4.tcp_keepalive_time;
2、缩短发送 Keepalive 探测包的间隔时间 net.ipv4.tcp_keepalive_intvl;
3、减少 Keepalive 探测失败后,一直到通知应用程序前的重试次数 net.ipv4.tcp_keepalive_probes。
如果开启了 TCP 保活,需要考虑以下几种情况:
第一种,对端程序是正常工作的。当 TCP 保活的探测报文发送给对端 对端会正常响应,这样 TCP 保活时间会被重置,等待下一个 TCP 保活时间的到来。
第二种,对端程序崩溃并重启。当 TCP 保活的探测报文发送给对端后,对端是可以响应的,但由于没有该连接的有效信息,会产生一个 RST 报文,这样很快就会发现 TCP 连接已经被重置。
第三种,是对端程序崩溃,或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,TCP 会报告该 TCP 连接已经死亡。
优化 TCP 性能时,要注意,如果同时使用不同优化方法,可能会产生冲突。
比如,就像网络请求延迟的案例中,服务器端开启 Nagle 算法,而客户端开启延迟确认机制,就很容易导致网络延迟增大。
另外,在使用 NAT 的服务器上,如果开启 net.ipv4.tcp_tw_recycle ,就很容易导致各种连接失败。实际上,由于坑太多,这个选项在内核的 4.1 版本中已经删除了。
UDP 提供了面向数据报的网络协议,它不需要网络连接,也不提供可靠性保障。所以,UDP 优化,相对于 TCP 来说,要简单得多。这里我也总结了常见的几种优化方案。
- 增大套接字缓冲区大小以及 UDP 缓冲区范围;
- 跟前面 TCP 部分提到的一样,增大本地端口号的范围;
- 根据 MTU 大小,调整 UDP 数据包的大小,减少或者避免分片的发生。
原文出处:https://mp.weixin.qq.com/s/fQ7xfnCUVRyw1JCgQ1zASQ