TCP传输速度慢但带宽空闲,多数情况不是物理链路拥堵,而是TCP自身的滑动窗口、拥塞控制、延迟确认等机制没匹配当前网络延迟,发送方主动“踩了刹车”。
为什么带宽显示空闲但TCP传输速度慢?先看TCP的“刹车系统”
TCP不是一发到底的协议,它靠滑动窗口决定“路上能有多少数据”,窗口太小,路再宽也跑不满。
- 带宽代表车道宽度。
- 延迟(RTT)代表一个数据包来回需要的时间。
- 丢包代表路上有坑,TCP一旦感知丢包就会大幅降速。
带宽延迟积(BDP)是判断窗口是否够用的关键公式:BDP = 带宽 × RTT,比如100Mbps带宽、50ms延迟,BDP约625KB,如果TCP窗口只有64KB,速度就只有理论带宽的十分之一左右,这就是典型的带宽空闲但TCP传输速度慢。
带宽空闲却网速慢,先查RTT和丢包
用ping连续测试:
ping -c 100 目标IP
重点看两个值:平均RTT和丢包率,RTT超过50ms就属于高延迟链路,跨地域服务器TCP传输速度慢经常是这个原因,丢包率哪怕只有很小比例,TCP也会触发重传和降窗,实际吞吐会大幅下滑。
抓包看重传:
tcpdump -i eth0 -s 0 host 目标IP -w tcp.pcap
然后用Wireshark或tshark过滤tcp.analysis.retransmission,也可以直接看系统统计:
netstat -s | grep -i retransmit
重传统计值持续增长,基本可以断定丢包在拖慢速度。
局域网TCP传输速度慢的常见原因:窗口和缓冲区没跟上
很多人在千兆局域网内测速只有三四十MB/s,第一反应是网线或交换机,其实多数情况下是TCP窗口和内核缓冲区太小。
Linux系统默认TCP缓冲区可能只有几百KB,低延迟局域网里问题不明显,一旦出现虚拟化、跨网段或无线网络,延迟升高后窗口立刻成为瓶颈。
服务器TCP优化参数:调大窗口和缓冲区
检查当前窗口和缓冲区:
sysctl net.ipv4.tcp_rmem sysctl net.ipv4.tcp_wmem sysctl net.core.rmem_max sysctl net.core.wmem_max

三个值分别代表最小值、默认值、最大值,如果最大值还不到几MB,就需要调整,在服务器TCP优化参数里加入以下配置:
net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 net.ipv4.tcp_window_scaling = 1
tcp_window_scaling=1是开启TCP窗口缩放,必须打开,否则窗口最大只能64KB,改完执行sysctl -p生效。
查看实际协商的窗口大小:
ss -tin 'dst 目标IP'
重点看snd_wnd和rcv_wnd字段,如果实际窗口接近接收缓冲区上限,说明瓶颈就在窗口。
用iperf3验证TCP速度,排除应用层干扰
iperf3是排查TCP传输速度慢什么原因的标准工具,服务端运行:
iperf3 -s -p 5201
客户端测试:
iperf3 -c 服务端IP -p 5201 -t 30 -P 4
-P 4表示4个并行流,多流速度总和接近带宽、单流速度上不去,基本就是窗口或延迟问题,多流也慢,再查网卡和丢包。
TCP慢启动怎么优化?让它别在起步阶段“磨蹭”
TCP慢启动是每个连接都要经历的过程:拥塞窗口从很小开始,每收到一个ACK增加一个MSS,呈现指数增长,大文件传输时,慢启动阶段看似短暂,但在高延迟链路上,窗口爬升到能跑满带宽需要好几个RTT,整体平均速度就被拉低。
调整初始拥塞窗口和路由缓存
查看当前拥塞控制算法:
sysctl net.ipv4.tcp_congestion_control
如果显示cubic,可以考虑切换到bbr,BBR对高延迟、有轻微丢包的链路更友好,不易被少量丢包“吓到”降速。
开启BBR:
modprobe tcp_bbr sysctl -w net.ipv4.tcp_congestion_control=bbr
持久化写入:
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
调整初始拥塞窗口(initcwnd)也能改善慢启动表现:
ip route show ip route change default via 网关IP dev eth0 initcwnd 32 initrwnd 32

initcwnd从默认的小值调高到32,可以让TCP起步阶段多发一些数据,减少短文件传输受慢启动的影响,但不要盲目调太大,否则可能加剧突发流量导致的丢包。
Nagle算法和延迟ACK的“小包互等”
交互式场景下,Nagle算法和延迟ACK同时开启会造成40ms左右的额外延迟,原因是Nagle规定小包必须等确认,而延迟ACK又故意等更多数据再确认,两者互相等待。
应用层可以设置TCP_NODELAY关闭Nagle,例如SSH、远程桌面、数据库连接,Nginx、Redis等部分服务默认就会设置。
Linux内核层面可以调整延迟ACK时间:
sysctl -w net.ipv4.tcp_delack_min=10
单位是毫秒,不建议调成0,会显著增加ACK包数量。
网卡队列、GRO/TSO和中断合并
网卡本身的配置也会限制TCP吞吐,尤其是虚拟化环境或云服务器。
查看网卡特性状态:
ethtool -k eth0
重点确认tcp-segmentation-offload、generic-receive-offload、large-receive-offload是否开启,这些硬件卸载功能可以把数据包处理从CPU转交给网卡,减少中断数量。
开启GRO/GSO/TSO:
ethtool -K eth0 gro on gso on tso on
调整发送队列长度:
ip link set dev eth0 txqueuelen 10000
云服务器通常无法直接改物理网卡参数,只能调整内核TCP参数。
常见场景速查表
| 场景 | 主要原因 | 优先检查项 | 调整方向 |
|---|---|---|---|
| 跨地域服务器TCP传输速度慢 | RTT高、窗口不足 | ping、ss -tin |
调大缓冲区、开启窗口缩放 |
| 千兆局域网内拷贝只有三四十MB/s | 应用未设置TCP_NODELAY、窗口不够 | iperf3单流测试 |
调整tcp_rmem、关闭Nagle |
| 小文件传输慢 | 慢启动、延迟ACK | 抓包看ACK延迟 | 提高initcwnd、调小delack |
| 大文件后段掉速 |
丢包触发拥塞控制 |
netstat -s看重传 |
切换BBR、排查链路丢包 |
| 虚拟化/云服务器TCP慢 | 网卡队列、CPU中断 | ethtool -S统计 |
开启GRO、TSO,调队列长度 |
排查TCP传输速度慢的实操步骤
- 用
ping -c 100 目标IP确认RTT和丢包。 - 用
iperf3 -c 目标IP -P 4区分单流和多流速度。 - 用
ss -tin查看实际窗口大小,对比BDP。 - 检查
sysctl net.ipv4.tcp_rmem等缓冲区配置。 - 用
tcpdump抓包分析重传和ACK是否延迟。 - 按需调整
tcp_window_scaling、tcp_rmem、tcp_wmem、tcp_congestion_control。 - 再次用iperf3复测,观察吞吐是否接近带宽。
Q&A:TCP传输速度慢但带宽空闲常见问题
问:为什么带宽充足但单线程TCP速度上不去,多线程却能跑满?
答:这通常是TCP滑动窗口不足或慢启动导致,单线程的拥塞窗口还没爬升到足够大,传输就结束了;多线程相当于多个人同时搬数据,总窗口叠加,自然能更快跑满,解决办法是调大内核缓冲区、开启窗口缩放、提高initcwnd。
问:局域网TCP传输速度慢和网线质量有关吗?
答:有一定关系,但不是首要因素,千兆局域网内如果网线劣质或水晶头接触不良,会产生丢包和CRC错误,TCP会因重传而降速,先看ethtool -S eth0里的rx_crc_errors和tx_errors,如果持续增长就更换物理链路,否则优先排查窗口和缓冲区配置。
问:服务器上已经开启了BBR,为什么TCP传输速度还是上不去?
答:BBR能优化拥塞控制,但不能改变物理延迟和丢包,如果RTT很高,窗口又没调大,带宽延迟积依然限制吞吐,还需要检查内核缓冲区、网卡队列、应用是否设置了TCP_NODELAY,带宽空闲但网速慢往往是多个因素叠加的结果。
TCP传输速度慢但带宽空闲,本质上不是带宽不够,而是TCP在“自我设限”,抓住窗口、延迟、丢包这三条线,再用iperf3和抓包逐项验证,多数问题都能定位到具体的参数或配置。
