本文要点
- 排查「连不上 / 连得慢 / 连接数只涨不跌」时,内核其实一直在记录三张表:socket 表(谁在连、什么状态、队列里堆了多少字节)、累计计数器(连了多少次、失败多少次、重传多少段)、每条连接的控制块(tcp_info:RTT、拥塞窗口、重传)。对应命令就是
ss、/proc/net/snmp、ss -ti——零安装、零抓包 ss -lnt里 LISTEN 行的 Recv-Q / Send-Q 不是收发队列:Recv-Q 是「已完成握手、还在 accept 队列里等应用取走」的连接数,Send-Q 是listen()的 backlog 上限。实测这台机器上 sshd 的 22 端口是128、systemd-resolved 的 53 端口是4096;Recv-Q 顶到 Send-Q 时新连接会被丢,现象是「超时」而不是「拒绝」ss -tan state established会漏掉真正要盯的僵尸连接:实测接收端没close()时连接停在CLOSE-WAIT,established 列表里查不到它,得看ss -tan全状态。CLOSE-WAIT 只增不减 = 应用忘了关连接ss只是内核 socket 表的翻译层,原始形态在/proc/net/tcp:地址列是小端十六进制、端口列是大端——3500007F:0035就是127.0.0.53:53;状态是数字码(0A=LISTEN、01=ESTABLISHED);inode列能把 socket 和进程对上号(socket:[inode]),IPv6 在/proc/net/tcp6/proc/net/snmp的Tcp:行是累计计数器(15 个数字、顺序固定,实测RetransSegs已经累计到7911),单看没有信息量,看的是变化量:nstat不带-a时只打印「自上次调用以来变化过的计数器」,所以第一次跑常常一片空白,要先nstat -az存一次基线- 一次可控小流量就能验证计数器怎么动:连一次本机端口 + 连一个没人监听的端口,实测增量正好是
TcpActiveOpens 2、TcpPassiveOpens 1、TcpAttemptFails 1、TcpInSegs 15/TcpOutSegs 13——AttemptFails 在涨,就是有客户端连不上 - 单条连接的深水区在
ss -ti的 tcp_info 续行(以 tab 开头的第二行):rto(重传超时)、rtt(平滑 RTT / 抖动)、cwnd(拥塞窗口)、ssthresh、unacked(在途字节)、retrans(重传次数) - 为了让「正常」有对照,用
tc netem在lo上注入 120ms 延迟 + 5% 丢包:实测 RTT 从1.114ms涨到254.63ms、cwnd从 27 降到 20,并冒出unacked:23和retrans:1/7——干净链路上这两个字段根本不出现(tcp_info 的字段是条件打印的) - 两个实测坑:
ss -ti的字段在续行里,只 grep 关键字容易只抓到表头;tc qdisc add dev lo root netem不能原地改,已有规则时报Error: Exclusivity flag on, cannot modify.,必须先tc qdisc del dev lo root - 全程零安装:
ss/nstat/tc都是 iproute2 自带,/proc/net/*是内核直接给的。1 核 1.9G、无 swap 的演示机上跑完全部三个演示,磁盘占用为 0,收尾时lo的 qdisc 已回到noqueue
Linux TCP 连接诊断:从 ss 到内核计数器
「服务连不上」「连得上但很慢」「连接数只涨不跌」——这三类问题的第一反应通常是抓包。抓包当然能定位,但它有个前提:你得先知道该盯哪条连接、哪个方向,否则在几十万行 pcap 里翻起来非常痛苦。
内核其实一直在记录。 它维护着三张随时可查的表:
- socket 表——有哪些连接、各自什么状态、队列里堆了多少字节、属于哪个进程;
- 累计计数器——总共发起/接受过多少连接、失败多少次、重传过多少段、收到过多少错误;
- 每条连接的控制块(
tcp_info)——这条连接的 RTT、重传超时、拥塞窗口、在途字节。
它们对应的入口分别是 ss、/proc/net/snmp、ss -ti,全是系统自带命令,不需要装包,也不需要抓包。而且开销极低:读的是一个内存里的哈希表和一个计数器数组,没有包捕获、没有 ptrace。
本文按「从全机到单条连接」的顺序走一遍:先看整机有哪些连接,再看内核原始的 socket 表和计数器,最后钻进一条连接的 TCP 控制块。每一步都在一台 1 核 1.9G、没有 swap 的 Ubuntu 22.04 小机器上实测(内核 5.15.0-30-generic、iproute2 5.15.0、iproute2 自带的 ss 5.15.0),踩到的坑会单独列出来。
包这一层的排查(ping、tcpdump、链路连通性)见 Linux 网络排查:从 ping 到 tcpdump 的故障定位手册,本文补的是它下面那一层——包已经进出去了,内核自己怎么记账。
全机视角:先看有几条连接
三个命令按「粗到细」排列:
ss -s # 按状态汇总的连接总数
ss -lnt # 监听中的 TCP 端口(l=listen, n=不做域名解析, t=TCP)
ss -tan # 全部 TCP 连接,含 TIME-WAIT、CLOSE-WAITss -s 是一眼能看完的概览,实测输出:
Total: 154
TCP: 4 (estab 1, closed 0, orphaned 0, timewait 0)
Transport Total IP IPv6
RAW 0 0 0
UDP 1 1 0
TCP 4 3 1
INET 5 4 1
FRAG 0 0 0三个数字各有口径:Total 是所有协议族的 socket 总数,TCP 那行按状态拆开(estab 只数 ESTABLISHED,TIME-WAIT 和 CLOSE-WAIT 都不算),下面那张表按协议 × 地址族二维展开。所以「总数 154,estab 只有 1」是正常的——一台只开着 SSH 的机器就是这样。
LISTEN 行的 Recv-Q / Send-Q 不是队列长度
这是 ss -lnt 里最容易被误读的地方:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*在非 LISTEN 的行里,Recv-Q / Send-Q 确实是这个 socket 的接收队列和发送队列字节数;但在 LISTEN 行里,含义完全变了:
- Recv-Q = 已完成三次握手、还排在 accept 队列里等应用
accept()的连接数; - Send-Q =
listen(fd, backlog)时给的 backlog 上限,不是「剩余还能接几个」。
实测这台机器上,sshd 的 22 端口 backlog 是 128,systemd-resolved 的 53 端口是 4096(Ubuntu 的单元文件里显式设过)。这两行都是「零 Recv-Q」,也就是没有积压。
当 Recv-Q 逼近 Send-Q,说明握手排满了、应用 accept 得太慢:内核会把新来的 SYN 丢掉(或交给 SYN cookie 兜住),客户端看到的是连接超时,而不是 Connection refused。这个区别很重要——「超时」让你去查网络,「队列满」才是真因。
state established 会骗你
ss 支持按状态过滤,语法很直白:
ss -tan state established # 只看已建立
ss -tan state syn-sent # 只看 SYN 发出、还没回应的(连不上的第一现场)
ss -tan state time-wait | wc -l # 数一下 TIME-WAIT 有多少(三个状态名在 iproute2 5.15 上都实测可用,返回码 0。)
但只看 established 会漏掉真正要盯的僵尸连接。实测中一次演示里接收端拿到连接后没持有 socket,连接立刻从 ESTABLISHED 掉到 CLOSE-WAIT:
CLOSE-WAIT 1 0 127.0.0.1:38422 127.0.0.1:8899ss -tn state established 查不到它,ss -tan 才看得到。这个状态的语义是「对端已经发来 FIN(它关了),本端应用还没 close()」——所以它只增不减就是应用在泄漏连接,是最典型的句柄泄漏现场。
TIME-WAIT 恰好相反:它是主动关闭方的正常收尾状态,量大有调优空间(tcp_tw_reuse 那一类,见 Linux sysctl 内核参数调优:从 /proc/sys 到 sysctl.conf 永久配置),和 CLOSE-WAIT 完全是两回事。
内核里的原始形态:/proc/net/tcp
ss 并不神秘,它读的就是内核的 socket 表,只不过替你翻译了一遍。翻译前的样子:
ss -lnt
echo '--- 内核里这张表的原始形态:地址是小端十六进制 ---'
head -1 /proc/net/tcp
grep -v '^ *sl' /proc/net/tcp | head -3
echo '--- 解码地址与状态码 ---'
python3 - <<'PY'
import socket, struct
S = {1: 'ESTABLISHED', 2: 'SYN_SENT', 6: 'TIME_WAIT', 8: 'CLOSE_WAIT', 10: 'LISTEN'}
for line in open('/proc/net/tcp').readlines()[1:4]:
f = line.split()
ip, port = f[1].split(':')
ip = socket.inet_ntoa(struct.pack('<I', int(ip, 16)))
print(f'{ip}:{int(port, 16):<6} st=0x{f[3]} {S.get(int(f[3], 16), "?")}')
PY
解码结果(截图最后三行):
127.0.0.53:53 st=0x0A LISTEN
0.0.0.0:22 st=0x0A LISTEN
172.16.0.84:22 st=0x01 ESTABLISHED三行对应三条 socket:systemd-resolved 的存根 DNS、sshd 的监听、以及我自己这条 SSH 会话(本机 172.16.0.84:22 ↔ 客户端)。几个要点:
- 地址是小端十六进制,端口却是大端:
3500007F:0035要按 4 字节倒序读(7F 00 00 35→127.0.0.53),端口0035直接按十六进制读就是 53。这个不对称是第一眼最容易读错的地方,解码脚本里struct.pack('<I', ...)处理的就是它。 - 状态是数字码:
st列的0A是 LISTEN、01是 ESTABLISHED。完整对照见下面的表。 tx_queue:rx_queue是这个 socket 的发送/接收队列字节数:LISTEN 行恒为 0;实测那条 SSH 连接是000002B4:00000000,也就是发送队列里排着 692 字节(0x2B4)。inode列能把 socket 和进程对上号:/proc/<pid>/fd/里那个socket:[259048]就是它——ss -p显示进程名靠的正是这条线索。排查「这个端口到底是谁占的」时,它是lsof之外的另一条路。- IPv6 在
/proc/net/tcp6(演示机上该文件存在、权限-r--r--r--),格式与tcp完全一致,只是地址是 32 位十六进制。
st 状态码全表(net/tcp_states.h 里的定义,十六进制):
| st | 状态 | 什么时候出现 |
|---|---|---|
| 01 | ESTABLISHED | 连接已建立,可以传数据 |
| 02 | SYN_SENT | 本端发了 SYN,等对端 SYN+ACK(连不上的第一现场) |
| 03 | SYN_RECV | 收到 SYN、回了 SYN+ACK,等最后一个 ACK |
| 04 | FIN_WAIT1 | 本端主动关闭,FIN 已发出 |
| 05 | FIN_WAIT2 | 本端 FIN 已被确认,等对端 FIN |
| 06 | TIME_WAIT | 双方都关了,等 2MSL 后彻底消失 |
| 07 | CLOSE | 完全关闭 |
| 08 | CLOSE_WAIT | 对端已关,本端应用还没 close() |
| 09 | LAST_ACK | 被动关闭方等最后一个 ACK |
| 0A | LISTEN | 正在监听 |
| 0B | CLOSING | 双方几乎同时关闭 |
累计计数器:/proc/net/snmp 与 nstat 的增量视图
/proc/net/snmp 里的 Tcp: 行是内核从开机起累计的 TCP 计数器,15 个数字,顺序是固定的:
grep '^Tcp:' /proc/net/snmp | tail -1实测这一行是:
Tcp: 1 200 120000 -1 462 48433 13 27218 1 847549 872048 7911 10 61079 0逐字段对照(值是上面的实测值):
| 字段 | 实测 | 含义 |
|---|---|---|
| RtoAlgorithm | 1 | 重传超时算法(1 = RFC 793 的经典算法) |
| RtoMin / RtoMax | 200 / 120000 | RTO 下限 / 上限(毫秒) |
| MaxConn | -1 | 最大连接数(-1 = 不限制) |
| ActiveOpens | 462 | 本机主动发起连接的次数(connect()) |
| PassiveOpens | 48433 | 本机被动接受连接的次数(服务被连) |
| AttemptFails | 13 | 连接尝试失败的次数 |
| EstabResets | 27218 | 已建立连接被 RST 掉的次数 |
| CurrEstab | 1 | 当前处于 ESTABLISHED 的连接数 |
| InSegs / OutSegs | 847549 / 872048 | 收 / 发的 TCP 报文段总数 |
| RetransSegs | 7911 | 重传的报文段总数 |
| InErrs / OutRsts | 10 / 61079 | 收到的错误段数 / 发出的 RST 数 |
| InCsumErrors | 0 | 校验和错误数 |
单看这行没有信息量——它是开机以来的累计值,OutSegs 是 87 万还是 8.7 万,取决于机器开了多久。有用的是变化量,而 nstat 就是干这个的:
nstat(不带参数):打印自上次调用以来变化过的计数器,没变的一律不显示;nstat -a:忽略历史文件,全部打印;nstat <名字>:只看指定的计数器(名字可以用正则)。
所以排查前要先「存一次基线」,否则第一次跑 nstat 往往是空的。完整演示:
CNT='TcpActiveOpens TcpPassiveOpens TcpAttemptFails TcpInSegs TcpOutSegs TcpRetransSegs'
echo '--- 1) 内核原始计数器:/proc/net/snmp 的 Tcp 行,字段顺序固定 ---'
grep '^Tcp:' /proc/net/snmp | tail -1
echo '--- 2) 先读一次存基线,把历史文件推到现在 ---'
nstat -az $CNT > /dev/null
echo '--- 3) 造流量:一次成功连接 + 一次连没人监听的端口 ---'
python3 - <<'PY'
import socket, threading
srv = socket.socket(); srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(('127.0.0.1', 8898)); srv.listen(4)
threading.Thread(target=lambda: [srv.accept()[0].recv(65536) for _ in range(50)], daemon=True).start()
c = socket.create_connection(('127.0.0.1', 8898))
c.sendall(b'y' * 200000)
c.close()
try: socket.create_connection(('127.0.0.1', 8897), timeout=1) # 端口没人听
except OSError: pass
PY
echo '--- 4) 再读一次:不带 -a 时 nstat 只打印变化过的计数器 ---'
nstat $CNT
第二步的 nstat -az > /dev/null 把当前值写进历史文件(相当于「按下秒表」),第三步制造流量,第四步读增量,输出正好是五个计数器:
#kernel
TcpActiveOpens 2 0.0
TcpPassiveOpens 1 0.0
TcpAttemptFails 1 0.0
TcpInSegs 15 0.0
TcpOutSegs 13 0.0这五个数字和刚才做的事严格对得上:
ActiveOpens +2:本机发起了两次connect()(一次成功、一次被拒);PassiveOpens +1:有一次连接在本机被接受(127.0.0.1:8898那个监听);AttemptFails +1:被拒的那次——对端回了 RST,连接尝试失败;InSegs +15 / OutSegs +13:两百多 KB 的本机往返加上那次失败的握手,一共 15 个收包、13 个发包。
这就是增量视图的价值:AttemptFails 在涨,等于「有客户端连不上」;RetransSegs 在涨,等于「有丢包」;EstabResets 在涨,等于「有连接被对端 RST 掐断」。它们的绝对值没有意义,斜率才有。
收尾提醒:nstat -az会把历史文件推到此刻,也就是消费掉了上一次的增量。生产环境里排查前想留证据,先跑一次nstat -az存档再动手。
单条连接的深水区:tcp_info
前两节都是「全机视角」,最后落到一条连接上。ss -i 会为每条连接多打出一行内核 TCP 控制块里的信息——注意它是以 tab 开头的续行,紧跟在 socket 行下面。这是本机一条真实 SSH 连接(字段太多,中间省略):
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 84 172.16.0.84:22 117.140.251.220:24411
cubic wscale:6,7 rto:240 rtt:36.088/2.118 … cwnd:10 …字段很多,先把最常看的挑出来:
| 字段 | 含义 | 怎么用 |
|---|---|---|
rto | 重传超时(毫秒) | 内核按 RTT 动态算出来的,通常不用直接看 |
rtt | 平滑 RTT / 平均偏差(毫秒) | 第一个数是 RTT,第二个数是抖动,判断「慢在哪一段」的第一手数据 |
cwnd | 拥塞窗口(报文段数) | 乘上 MSS 就是在途字节上限;带宽延迟积大而 cwnd 上不去,说明被拥塞控制了 |
ssthresh | 慢启动阈值 | 掉到很低说明刚经历过丢包 |
unacked | 已发出未被确认的字节数 | 连着 retrans 一起看,判断是否卡在恢复阶段 |
retrans | 重传数(当前窗口 / 累计) | 增长就是丢包,最直接的证据 |
segs_out / segs_in | 发出的 / 收到的报文段数 | 判断数据往哪个方向流 |
问题在于,一条正常连接的 tcp_info 里没有「异常」可看——所以演示时得先造出一个「异常」当对照。tc netem 可以在不碰物理网卡的前提下给回环设备加延迟和丢包(lo 上的规则只影响本机回环,SSH 走的是 eth0,不会被自己坑到),完整脚本:
tc qdisc del dev lo root 2>/dev/null # 先清干净:netem 不能原地改,残留规则要先删
cat > /tmp/tcpinfo.py <<'PY'
import socket, subprocess, threading, time
# 按对端端口锁定本机这条连接,取它后面那行内核 tcp_info,只留关心的字段
SS = ("ss -tin | awk '$5 ~ /:8899$/ {getline; print; exit}' "
"| tr ' ' '\n' | grep -E "
"'^(rto|rtt|cwnd|unacked|retrans|segs_out|segs_in|ssthresh)' | paste -sd' '")
srv = socket.socket(); srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(('127.0.0.1', 8899)); srv.listen(8)
def serve(): # 一直收,别让接收窗口把发送端卡住
while True:
try: c, _ = srv.accept()
except OSError: return
threading.Thread(target=lambda s=c: [s.recv(262144) for _ in range(99999)],
daemon=True).start()
threading.Thread(target=serve, daemon=True).start()
def blast(sec): # 持续灌数据,tcp_info 才有东西可看
c = socket.create_connection(('127.0.0.1', 8899))
end = time.time() + sec
while time.time() < end:
try: c.sendall(b'x' * 65536)
except OSError: break
return c
def snap(label):
out = subprocess.run(SS, shell=True, capture_output=True, text=True).stdout.strip()
print(f'{label} -> {out}')
c1 = blast(3); snap('干净 lo 链路'); c1.close(); time.sleep(0.5)
subprocess.run('tc qdisc add dev lo root netem delay 120ms loss 5%', shell=True)
c2 = blast(3); snap('延迟 120ms + 丢包 5%'); c2.close()
PY
python3 /tmp/tcpinfo.py; rm -f /tmp/tcpinfo.py
tc qdisc del dev lo root 2>/dev/null; echo '已清理 netem 与临时脚本'
两次快照的实测结果:
干净 lo 链路 -> rto:204 rtt:1.114/0.897 cwnd:27 ssthresh:19 segs_out:13596 segs_in:3366
延迟 120ms + 丢包 5% -> rto:488 rtt:254.63/26.049 cwnd:20 ssthresh:14 segs_out:201 segs_in:63 unacked:23 retrans:1/7对照着读,信息量比想象的大:
rtt从 1.114ms 变成 254.63ms:注入的是 120ms 单程延迟,往返就是 240ms 上下,实测值和注入值吻合。第二个数(/0.897→/26.049)是平均偏差,也就是抖动——它从不到 1ms 涨到 26ms,说明这条链路不只是慢,而且不稳定。cwnd从 27 降到 20:拥塞窗口被丢包打下来了。丢包时内核会把ssthresh砍半(19 → 14)并缩小 cwnd,这是拥塞控制的正常反应,不是故障。unacked:23冒出来了:有 23 个报文段(在途字节)还没被确认。干净链路上这个字段根本不出现——因为当时它的值是 0。retrans:1/7冒出来了:当前窗口重传了 1 个,累计重传 7 个。这是「链路在丢包」最直接的证据。segs_out从 13596 掉到 201:同样的 3 秒,干净链路上发了 1.3 万个报文段,加了 240ms RTT 之后只发了 201 个——吞吐的差距不在带宽,在往返时间。
最后一条是这组数字最实用的地方:如果对端抱怨「你们接口慢」,而 rtt 显示 250ms、retrans 还在涨,那问题在链路上,不在应用;反过来 rtt 正常但 cwnd 一直很小、retrans 持续增长,就要去查中间设备了。
字段是条件打印的:retrans和unacked只在非零时出现,delivery_rate这类也不总有。解析脚本里「字段取不到」很可能不是 bug,而是它的值本来就是 0——别写死字段个数。
一张排查路径表
把三层证据按「现象」组织起来,比背参数有用:
| 现象 | 先看哪里 | 命令 |
|---|---|---|
Connection refused | 对端有没有在监听;本端失败计数有没有涨 | 对端 ss -lnt,本端 nstat TcpAttemptFails |
| 连接卡住不动(发出去的 SYN 没回应) | 本端有没有卡在 SYN_SENT | ss -tan state syn-sent |
| 连得上但很慢 | 这条连接的 RTT 与重传 | ss -ti 看 rtt / retrans / cwnd |
| 服务端「接不过来」 | LISTEN 行的 Recv-Q 是否逼近 Send-Q | ss -lnt |
| 连接数只增不减 | 是否堆在 CLOSE-WAIT(应用没关连接) | ss -tan(别只看 established) |
| 整机网络异常 | 计数器斜率:失败、重传、RST | nstat -az 存基线 → 隔一会儿 nstat |
坑清单(都是本机实测)
ss -ti的字段在续行里。tcp_info 是紧跟在 socket 行下面的、以 tab 开头的一行,grep关键字很容易只抓到表头。稳妥的取法是先按端口定位 socket 行、再取它的下一行——本文脚本用的是awk '$5 ~ /:8899$/ {getline; print; exit}',先匹配「对端端口是 8899 的那条 socket」,再读它的下一行。state established会漏掉 CLOSE-WAIT。僵尸连接不在 established 里,排查连接泄漏要看ss -tan全状态(ss -s的estab字段同样只数 ESTABLISHED)。nstat不带-a时「没变化就不打印」。第一次跑常常一片空白,会让人以为计数器坏了;要先nstat -az存基线。反过来,-az会把历史推到此刻,等于消费掉上一次的增量。- LISTEN 行的 Send-Q 不是发送队列,是
listen()的 backlog 上限;Recv-Q 才是「等 accept 的连接数」。 tc qdisc add不能原地改 netem。已有规则时实测报Error: Exclusivity flag on, cannot modify.(返回码非 0),必须先tc qdisc del dev lo root。本文脚本第一行就是这句删除,就是为了避开它;lo的默认 qdisc 是noqueue,删掉 netem 之后会回到它。- 演示完一定要把 netem 删掉。残留的 120ms 延迟会让本机所有回环通信一起变慢(包括 systemd-resolved 的
127.0.0.53);本次第一轮演示就踩了这个坑——上一轮脚本中途报错退出,没走到清理那一步,导致下一轮「干净链路」的 RTT 直接是 251ms。排查时看到的「网络慢」,有可能是自己上一条命令留下的。
小结
TCP 诊断的三层证据,各自回答一个不同的问题:
ss -tan/ss -lnt——「现在有哪些连接、什么状态、队列堆了多少」。用来确认现象(连不上、连得慢、连接堆积),一两秒就能看完;/proc/net/snmp+nstat——「一段时间内发生了什么」。先nstat -az存基线,做事,再nstat读增量,看的是斜率不是绝对值;ss -ti的 tcp_info——「这一条连接到底怎么了」。rtt定位慢在哪一段,retrans确认丢包,cwnd/unacked判断拥塞状态。
顺序是先粗后细:先确认有没有连接、再看计数器有没有异常增长、最后才钻到单条连接。反过来的话,很容易在一堆正常的连接里找一个不存在的异常。
全程零安装、零抓包、磁盘零占用——一台 1 核 1.9G 的小机器就能把这些全部跑一遍,收尾把 lo 上的 netem 删掉即可(本文演示机结束时 lo 的 qdisc 是 noqueue,磁盘可用 5.1G,无任何残留)。
想继续往下走:tc netem 还能注入抖动、乱序、重复包,玩法见 Linux 网络模拟:tc 与 netem 注入延迟、抖动与丢包;要逐包看握手细节就轮到 Linux 网络排查:从 ping 到 tcpdump 的故障定位手册;只是想确认某个端口通不通,netcat 网络调试工具:从端口探测到文件传输 比 ss 更顺手;而 tcp_tw_reuse、somaxconn 这些和本文直接相关的内核参数,在 Linux sysctl 内核参数调优:从 /proc/sys 到 sysctl.conf 永久配置 里有完整清单。
评论 (0)
暂无评论,快来抢沙发吧!