从工作第一天起,我就和 TCP、UDP 这两兄弟打交道,一晃十几年过去,期间被三次握手、端口占用、拥塞控制这些问题反复折腾过。身边也总有同事拿着抓包文件过来问:为什么这个连接一直处于 SYN_SENT?为什么 UDP 丢包这么严重?这让我意识到,很多人对 TCP/UDP 协议和端口的理解只停留在“知道名字”的层面,一到现场排查就抓瞎。
所以这篇博文我不打算讲教科书上的定义,而是把这些年在实际项目里积累的协议选型思路、端口排查套路、抓包分析经验,以及一些文档里不会写清楚的坑,一次性分享出来。无论你是刚入门的嵌入式开发者、做上位机软件的工程师,还是负责运维排查的服务端同学,这篇文章都值得花二十分钟从头看一遍,里面的操作命令和排查思路基本都是可以直接拿去用的。
1. TCP与UDP的本质区别与选型思路
1.1 从生活场景理解TCP和UDP
很多人第一次接触 TCP 和 UDP 时,都被“面向连接”“无连接”“可靠传输”“不可靠传输”这些术语绕晕了。其实用生活中的场景类比一下就很好理解。
TCP 相当于打电话。拨号、等待对方接听、确认双方都在线,才开始说话;通话过程中任何一句话没听清,都会说“你再说一遍”;通话结束后双方都要挂断。这种机制保证了内容的完整可靠,但代价是过程繁琐、效率受限。
UDP 相当于对讲机。你按下按键就能说话,不需要确认对方在不在听;你说完就完事,对方有没有收到、有没有听清,你完全不管。这种模式效率极高,但内容可能丢失、顺序可能错乱。
从技术层面看,TCP 提供了可靠字节流、流量控制、拥塞控制、丢包重传和顺序保证,而 UDP 只负责把数据包从一端发送到另一端,除此之外不做任何额外工作。所以 TCP 的开销更大,头部是 20 字节,UDP 头部只有 8 字节。
1.2 实际项目里怎么选
选 TCP 还是 UDP,不是拍脑袋决定的,得看业务场景的具体需求。
如果数据不能丢、不能错、顺序必须一致,比如文件传输、网页访问、数据库操作、远程登录、工业控制中的参数下发,这些场景必须用 TCP。以 Modbus TCP 为例,它在工业现场很常见,每次读写寄存器都要求严格的应答确认,用 UDP 的话一旦丢包就会导致设备状态不确定,这是绝对不允许的。
如果场景更看重实时性、允许少量丢包,比如音视频通话、直播推流、游戏同步、传感器数据采集,用 UDP 会更合适。你会发现即使视频通话卡顿了几帧,影响也不大,但要是为了可靠重传造成延迟累计,体验反而更差。语音不流畅比偶尔模糊更致命,这就是在 VoIP 系统中普遍选择 UDP 的原因。
项目里还有一个判断依据:单个请求的数据量大小和交互频率。高频小数据交互用 UDP 更划算,因为省去了连接建立和拆除的额外开销;低频大数据或强交互流程用 TCP,确保每一步都可靠落地。
1.3 混合使用也很常见
实际工程里并不一定要二选一。现在很多高性能系统采用了“混合模式”:控制信令走 TCP,多媒体数据走 UDP。比如很多 IoT 设备,设备注册、指令下发、状态查询走 TCP 保证可靠,而视频数据流走 UDP 保证实时,两者互不干扰。这种方式在工业机器人、AGV 调度系统里特别常见,项目选型时可以考虑这种思路。
注意:有人说 TCP 比 UDP 更省流量,这个说法不准确。TCP 的确认包、握手包、重传包确实会增加额外流量,但真正决定流量大小的是应用层数据。如果传输的文件本身有大量冗余,反而可以用压缩来省流量,这和用 TCP 还是 UDP 没有直接关系。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 端口机制详解与排查套路
2.1 端口到底是什么
端口可以理解成一台服务器上的“门牌号”。IP 地址负责找到是哪台机器,端口负责找到这台机器上的哪个进程。数据包到达服务器网卡后,内核根据 TCP/UDP 头部的目标端口号,把数据交给对应的应用进程。
端口号是 16 位的,范围从 0 到 65535。其中:
- 0 到 1023 是公认端口(Well-Known Ports),比如 21 是 FTP、22 是 SSH、80 是 HTTP、443 是 HTTPS。
- 1024 到 49151 是注册端口,供用户进程或应用程序使用,比如 MySQL 默认 3306、Redis 默认 6379、RabbitMQ 默认 5672。
- 49152 到 65535 是动态或私有端口,通常作为客户端临时端口使用。
这里插一句,很多初学者容易混淆“进程”和“端口”的关系。一个进程可以监听多个端口,一个端口也只能被一个进程监听(除非设置了 SO_REUSEPORT)。排查问题时,找到“哪个进程占了哪个端口”是最关键的一步。
2.2 Linux下排查端口占用
“端口被占”应该是运维和开发遇到最频繁的问题之一。很多热词里提到的 Linux 9090 端口、宝塔面板端口、Docker 映射端口冲突,排查方法都是一套。
最常用的三条命令是 ss、lsof 和 netstat。现在 netstat 在很多新系统里已经不预装了,所以我更推荐 ss,它的效率更高,信息也更全。
bash复制# 查看某个端口是否被监听,比如9090
ss -tlnp | grep 9090
# 查看所有TCP监听端口
ss -tlnp
# 查看所有UDP端口
ss -ulnp
# 通过进程名反查端口
lsof -i -P -n | grep nginx
看到输出后,重点关注 State 为 LISTEN 的行。如果某个端口状态是 ESTABLISHED,说明这是已建立的连接,而不是监听端口,别搞混了。
定位到占用端口的 PID 后,可以用下面的命令查看是哪个进程:
bash复制ps -ef | grep <PID>
如果是自己起的服务,直接杀掉进程重新启动就行。如果发现端口被一个不认识的服务占用,先确认是不是系统服务,再决定是否处理。我曾经在一台测试服务器上发现 9090 端口被一个监控组件占用,排查了半天才发现是同事之前装的 Node.js 应用没关。
2.3 Docker端口映射冲突的解决办法
Docker 场景下的端口占用问题比较特殊,报错信息一般是:
text复制Error response from daemon: ports are not available: exposing port TCP 0.0.0.0:8080: bind: address already in use
本质原因是宿主机上的 8080 端口已经被某个进程占用了,Docker 想通过 -p 8080:80 映射到宿主机端口时发现绑不上。
处理思路有两个方向:
- 换一个宿主机端口映射,比如
-p 8081:80。 - 找到并释放宿主机的 8080 端口。
排查命令和上面一样,先用 ss -tlnp | grep 8080 找到 PID,再决定处理方式。不建议直接重启 Docker 服务,因为核心问题是端口冲突,重启 Docker 大概率没法解决。
注意:Docker 容器内部的服务监听地址必须是 0.0.0.0 或者容器对应的具体 IP,不能只监听 127.0.0.1,否则宿主机端口映射暴露不出来。很多人遇到“容器启动了但宿主机访问不了”的问题,根因就是这个,别一上来就怀疑防火墙。
3. TCP核心机制与实操要点
3.1 三次握手背后到底发生了什么
TCP 是面向连接的协议,通信双方在传输数据之前必须先建立一个连接。这个建立过程就是“三次握手”。
正常流程是:
- 客户端发送 SYN 报文,随机生成一个初始序列号(比如 x),进入 SYN_SENT 状态。
- 服务端收到后回复 SYN+ACK 报文,确认号是 x+1,并带上自己的初始序列号 y,进入 SYN_RCVD 状态。
- 客户端收到后回复 ACK 报文,确认号是 y+1,双方进入 ESTABLISHED 状态。
从应用层的角度,用 Socket API 来看更直观:客户端调用 connect() 时触发握手,服务端调用 accept() 返回新连接。也就是说,三次握手对应用层是透明的,程序员只需要在合适的时机调用对应的 API。
之前有人问过我:为什么握手要三次,不是两次或者四次?这个问题展开说比较复杂,核心原因是为了确认双方的收发能力都正常,并且同步好初始序列号。如果只有两次握手,服务端无法确认客户端是否收到了自己的 SYN+ACK,可能会出现“服务端以为连接建立了,客户端实际上没有收到响应”的半连接状态。四次握手当然也可以,但三次已经足够了,第四次握手反而是多余的。
3.2 四次挥手为什么是四次
断开连接时,TCP 需要四次挥手,因为 TCP 连接是全双工的,也就是说数据可以同时双向传输。四次挥手必须保证两个方向都独立关闭。
第一次挥手:主动关闭方发送 FIN 报文,表示“我的数据发完了”。
第二次挥手:被动关闭方回复 ACK,表示“收到你的 FIN 了”。
第三次挥手:被动关闭方在发送完自己的数据后,发送 FIN 报文,表示“我的数据也发完了”。
第四次挥手:主动关闭方回复 ACK,表示“知道了”。
如果你用抓包工具看过挥手过程,会发现第二次和第三次挥手之间通常有一个时间差,这段时间就是被动关闭方在清理数据和发送剩余数据的阶段。这就是为什么挥手需要四次,而不是像握手那样三次就够。
不过在有些场景下,如果被动关闭方在收到 FIN 时已经没有数据要发了,内核可能会把 ACK 和 FIN 合并成一个报文发送,看起来就成了“三次挥手”。这种情况是优化过的行为,不是标准流程的异常。
3.3 实测抓包看状态变化
纸上谈兵没用,建议你亲自抓一次包。用最简单的 Python 代码起一个 TCP 服务端,再用客户端连接,配合 tcpdump 抓包看状态流转。
python复制# 服务端
import socket
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.bind(('0.0.0.0', 12345))
srv.listen(5)
conn, addr = srv.accept()
print(f"连接来自: {addr}")
data = conn.recv(1024)
print(f"收到: {data}")
conn.close()
srv.close()
bash复制# 服务端抓包
tcpdump -i any tcp port 12345 -n -S
运行后你会看到 SYN、SYN-ACK、ACK 三个报文,状态从 LISTEN 变成 SYN_RCVD 再到 ESTABLISHED。关闭连接时能看到 FIN、ACK、FIN、ACK 四个报文。亲手看一遍比背十遍状态图印象都深。
抓包过程中有个小技巧:加 -S 参数可以显示绝对序列号,方便你把握手过程中的序列号和确认号对应起来。如果不用这个参数,tcpdump 会显示相对序列号,看起来是 0、1,虽然直观,但不利于理解协议的原始机制。
3.4 拥塞控制与TCP性能调优
TCP 的拥塞控制是保证网络稳定性的核心机制。通俗地说,TCP 会根据网络状况动态调整发送速率,如果网络拥塞了,就主动降低发送速度,避免把网络打得更惨。
具体来说,拥塞控制经历了几个阶段:慢启动、拥塞避免、快速重传、快速恢复。每次 TCP 连接建立后,发送方先用一个很小的窗口慢慢试探网络,收到确认后指数增长,一直到慢启动阈值(ssthresh),然后进入线性增长阶段。一旦检测到丢包,就认为网络拥塞,立即降低发送窗口,重新开始试探。
这个机制在绝大多数场景下是好的,但也带来一个实际应用的痛点:TCP 的传输速度受网络延迟影响特别大。在跨地域网络环境下,如果 RTT(往返时延)很高,即使带宽充足,TCP 也要经历漫长的“慢启动”阶段才能把速率提上来。所以遇到 TCP 传输慢的问题,先不要怀疑代码,先看一下 RTT 和重传率。
查看链路质量可以用 ping 测 RTT,用 ss -ti 看 TCP 连接的往返时延和拥塞窗口:
bash复制# 查看当前服务器所有TCP连接的详细状态
ss -ti
# 只看某一端口的连接
ss -ti sport = :12345
输出信息里会包含 cwnd(拥塞窗口)、rtt(往返时延)、retrans(重传次数),这些指标能帮你快速判断链路是否存在瓶颈。如果 rtt 很高、重传频繁,业务侧调优再努力也没用,得从网络层面解决。
4. UDP核心机制与踩坑记录
4.1 UDP的优势与局限
UDP 没有三次握手,没有拥塞控制,没有重传机制。你可以随时向任意目标发送数据,不需要提前建立连接,也不需要等待确认。这让 UDP 在实时性要求高的场景下优势明显。
它的局限性也很突出。数据可能丢失、乱序、重复到达,而且 UDP 没有流量控制,发送端如果不管带宽限制疯狂发包,很容易造成网络拥塞甚至对端缓冲区溢出。很多新手用 UDP 写程序时,自己发的数据量不大,以为没问题,但实际上如果局域网内有大量广播包,或者设备本身处理不过来,丢包率可能远超预期。
UDP 还支持广播和组播,这是 TCP 完全不支持的。广播可以发给同一网段的所有设备,组播可以加入同一组的所有成员。这个特性在局域网设备发现、视频分发、工业现场控制等场景下特别有用。
4.2 UDP应用场景:从LabVIEW到嵌入式设备
热词里出现不少 UDP 相关的实际场景,比如 LabVIEW 怎么做 UDP 通信、Codesys 怎么用 UDP、ESP01S 怎么发 TCP/UDP 消息、ROS 机器人分发协议是不是 UDP。这些场景覆盖了上位机软件、PLC 编程、嵌入式开发、机器人系统,可见 UDP 在工业与物联网领域的应用面非常广。
以 LabVIEW 为例,它提供 UDP Open、UDP Write、UDP Read 等函数,本质上就是在系统 Socket 上做了一层封装。使用 UDP 通信时,需要注意几个容易踩的坑:
- UDP 是无连接协议,没有自动黏包处理,发送端的每次 Write 对应接收端的一次 Read,但边界不一定一致,需要应用层设计好帧格式。
- UDP 数据报有长度上限,正常情况下建议控制在 1472 字节以内(1500 MTU 减去 20 字节 IP 头再减去 8 字节 UDP 头),超过这个值就得依赖 IP 分片,分片丢包率更高。
- 如果收发双方不在同一个网段,需要确保路由器支持 UDP 转发,有些网络设备默认会过滤广播或组播报文。
在嵌入式场景中,ESP8266/ESP01S 这类 WiFi 模块也是 UDP 的常客。模块通过 AT 指令发送 UDP 数据时,需要先设置目标 IP 和端口,然后一条条地发送。这个场景下最需要注意的是对端地址的匹配:如果对端 IP 变了,模块可能还保持着旧地址的映射,导致数据发不出去。所以嵌入式里做 UDP 通信,应用层最好加一个心跳或探测机制,主动更新对端状态。
ROS 机器人系统确实大量使用了 UDP,但这取决于具体配置。ROS 1 默认的 XMLRPC 通信走 TCP,而话题数据的传输机制可以选择 UDP,尤其是大数据的传感器数据(比如激光雷达点云),用 UDP 可以降低传输延迟。ROS 2 则进一步优化,默认使用基于 UDP 的 DDS 协议。所以“机器人的协议是不是 UDP”这个问题不能简单用是或否回答,要看具体用的是哪一层、哪个中间件。
4.3 用 iperf3 实测 UDP 打流
热词里频繁出现“iperf3 使用 UDP 打流”,这里我给出一套可以直接抄的实测方法。
iperf3 是常用的网络性能测试工具,既能测 TCP 也能测 UDP。它的 UDP 模式可以通过指定带宽来测试网络的丢包率、抖动和吞吐量。
服务端(接收流量的一端):
bash复制iperf3 -s -p 5201
客户端(发送流量的一端):
bash复制# 以 100Mbps 的速率向 192.168.1.100 发送 UDP 数据
iperf3 -c 192.168.1.100 -u -b 100M -t 30 -p 5201
参数解释:
-u指定使用 UDP。-b指定目标带宽,iperf3 会尽量按这个速率发送。-t指定测试时长,单位秒。-p指定端口,默认 5201。
测试完毕后,客户端会输出吞吐量、丢包率和抖动数据。其中丢包率是关键指标。如果设置为 100Mbps 打流时丢包率超过 1%,说明链路或设备处理能力有瓶颈;如果丢包率特别高,先检查是不是对端 CPU 处理不过来,再检查交换机端口和网线质量。
这里有个实操心得:UDP 打流时,发送端的 -b 参数不是越大越好,因为 iperf3 默认会受到系统缓冲区大小限制。如果发现实际吞吐远低于目标带宽,可以加上 -l 1400 指定包大小,或者用 -w 4M 增大 Socket 缓冲区。实测下来,在千兆网环境里,用 1400 字节包大小、200Mbps 目标带宽测试,丢包率通常能控制在极低水平。
注意:UDP 打流测试会对网络造成较大压力,建议在业务低峰期做,并且先小带宽测起,逐步加大,避免影响线上业务。
5. 实战工具与常见问题排查技巧
5.1 抓包工具怎么选
排查网络问题,抓包分析是最有力的手段。工欲善其事,必先利其器,我常用的工具是 Wireshark 和 tcpdump。tcpdump 负责在服务器上抓包,生成 pcap 文件后拉到本地用 Wireshark 分析。
抓包时的过滤表达式很简单,但很多人上手时容易一脸懵。先把最常用的几个记住:
bash复制# 抓取某个端口的TCP流量
tcpdump -i eth0 tcp port 8080 -w capture.pcap
# 抓取某个IP的所有流量
tcpdump -i eth0 host 192.168.1.100 -w capture2.pcap
# 抓取UDP某个端口的流量
tcpdump -i eth0 udp port 6000 -w capture3.pcap
抓到 pcap 文件后,用 Wireshark 打开,重点看 TCP 的 Flags 列、Seq 和 Ack 列。排查重传问题时,用 Wireshark 的“分析 -> TCP 流图 -> 时间序列图”能直观看到窗口变化。如果你发现某个连接的窗口经常为 0,说明接收端缓冲区满了,应用层处理不过来,应该优化对端的数据处理逻辑而不是网络本身。
5.2 常见问题速查表:从握手到挥手
下面这份速查表基本覆盖了我这几年遇到的高频问题,遇到类似情况可以先按这个思路排查:
| 问题现象 | 可能原因 | 排查命令/方法 |
|---|---|---|
| 连接一直 SYN_SENT | 对端防火墙拦截、对端服务未启动 | ss -tn 看状态;telnet IP 端口 测试 |
| 连接一直 SYN_RCVD | 服务端 accept 队列溢出、SYN Flood 攻击 | ss -lnt 看 Send-Q 是否积压;调整 listen backlog |
| 连接建立后立即断 | 对端应用主动关闭、KeepAlive 超时 | 抓包看 FIN 发起方;检查应用日志 |
| UDP 丢包严重 | 带宽超限、接收缓冲区太小、CPU 处理不过来 | ss -unap 看 receive buffer;netstat -su 看丢包计数 |
| 端口被占 | 服务未退出、端口冲突 | ss -tlnp 查 PID,杀进程或换端口 |
| 服务能启动但外部访问不了 | 监听地址错误、防火墙未放行 | ss -tlnp 看监听地址;firewall-cmd --list-ports |
| TCP 传大文件速度上不去 | 拥塞窗口增长慢、RTT 过高 | ss -ti 看 rtt/cwnd;考虑调大初始窗口 |
| 应用间通信偶发超时 | 网络抖动、TCP 重传超时 | 抓包看乱序和重传;ping -f 压测链路 |
5.3 工业网络中的TCP/UDP实例分析
热词里有一些我印象特别深的工业场景问题,比如“西门子 TCP 只有每次重启的时候才能连上一分钟”,这个问题在我的排查生涯里遇到过类似的。
出现“只能连上一分钟”往往不是因为协议本身,而是连接建立后,通信过程中发生了异常导致连接被关闭,而应用没有正确处理重连逻辑。比如 PLC 作为客户端连接上位机,上位机的 TCP Server 只 accept 了一次,连接断开后没有重新回到 accept 等待状态;又比如通信数据里出现了未按 Modbus 帧格式解析的字节,导致异常退出。
排查思路应该是:
- 抓包看连接断开前发生了什么,是收到了 RST 还是 FIN。
- 查看应用日志,看是否有超时或解析异常。
- 检查是否有看门狗机制在定时复位通信模块。
另一个例子是“Modbus RTU 和 Modbus TCP 的区别”。Modbus RTU 跑在串口上,是二进制帧格式,使用 CRC 校验;Modbus TCP 跑在以太网上,把原本的地址和 CRC 去掉,换成 MBAP 头,依赖 TCP 保证可靠性。简单说,两者应用层数据格式类似,但传输层机制完全不同。做网关转换时,要注意字节序和处理超时,TCP 侧收到完整帧后立即转发到串口,串口侧收到响应后马上回 TCP,这是最常见的实现思路。
5.4 安全视角:别让端口成为攻击入口
端口是网络通信的入口,也是攻击面。常见的安全风险包括:端口扫描探测、UDP Flood 攻击、TCP SYN Flood 攻击、暴力破解尝试。
这里不教怎么发起攻击,但防御思路必须掌握。
针对端口扫描,最简单有效的方式是用防火墙限制来源 IP。对于对外开放的服务,建议只放行必要端口,其他端口一律关闭;对于管理端口(如 SSH),建议加上 IP 白名单或改用证书认证。
针对 UDP Flood,可以在防火墙上限制单 IP 的 UDP 速率,也可以在内核参数里调整 UDP 接收缓冲区大小,减少丢包对业务的冲击。实际部署的时候,可以用 sysctl -w net.core.rmem_max=8388608 这类命令加大缓冲区,但这只是缓解手段,治本还是要靠流量清洗设备或云服务商的 DDoS 防护。
针对 TCP SYN Flood,调整内核的 tcp_max_syn_backlog、tcp_synack_retries 参数可以临时缓解,更进一步可以启用 SYN Cookies(net.ipv4.tcp_syncookies=1)。但要注意,这些调优措施会稍增加 CPU 开销,属于“保命”级别的防护,生产环境调整需要谨慎评估。
注意:安全排查时,如果发现服务器有大量陌生端口的 UDP 发包或收包,先看是否中了挖矿木马或流量代理。用
ss -unp和ps -ef交叉排查,确认是已知业务再放行,否则立刻断网处理。
5.5 常用的网络调试小命令
除了抓包工具,日常排查还会用到几个“小工具”,虽然简单,但用好了能节省大量时间。
nc(Netcat)可以快速测试 TCP/UDP 端口连通性:
bash复制# 测试TCP端口
nc -zv 192.168.1.100 8080
# 监听UDP端口并打印收到的数据
nc -u -l 6000
telnet 也可以测 TCP 端口,但遇到 UDP 或者需要自定义包内容的场景就不行了。这时候可以用 nc 配合管道直接发送数据:
bash复制# 发送UDP数据包
echo "hello" | nc -u 192.168.1.100 6000
如果是在 Windows 环境,可以用 PowerShell 的 Test-NetConnection 来测试端口连通性。许多年前我需要快速验证服务器端口放行情况,都会在 Windows 机器上跑一条命令:
powershell复制Test-NetConnection 192.168.1.100 -Port 8080
跨平台开发的时候,还可以用 ping 和 traceroute 判断基础网络连通性和路径。链路不通的时候,ping 不通不一定代表端口不通,还可能是 ICMP 被禁了,所以最终判断还是要落到 TCP/UDP 端口测试上。
6. 协议栈调优的进阶建议
前面讲的都是基础排查和工具使用。真正到了生产环境,TCP/UDP 的调优又是一个大话题。这里分享几个踩过坑后总结的进阶建议。
第一,不要盲目修改内核参数。很多人网上一搜“TCP 优化”,不管三七二十一就改一堆 sysctl 参数,结果问题没解决,反而把网络搞得更糟。调优之前一定要先测量,明确瓶颈在哪里。是在带宽、延迟、缓冲区,还是应用处理能力?各有各的解法,不能一把抓。
第二,TCP 连接数特别大的场景,建议调整文件描述符限制和端口范围:
bash复制# 临时修改
sysctl -w net.ipv4.ip_local_port_range="1024 65535"
sysctl -w fs.file-max=2097152
但要记住,这只是临时修改,重启后失效,生产环境要改配置文件并做充分验证。
第三,UDP 应用在高负载下要关注内核丢包统计。netstat -su 输出的 UDP 部分里如果看到 packet receive errors 持续上涨,说明接收缓冲区不够,需要调大 net.core.rmem_default 和 net.core.rmem_max,同时在应用层用 setsockopt 设置 SO_RCVBUF。
第四,长连接场景建议开启 TCP KeepAlive,但要注意默认参数是两小时才探测一次,对很多业务来说太久。可以结合内核参数调整:
bash复制# 开启TCP KeepAlive
sysctl -w net.ipv4.tcp_keepalive_time=120
sysctl -w net.ipv4.tcp_keepalive_intvl=30
sysctl -w net.ipv4.tcp_keepalive_probes=3
这套配置的意思是连接空闲 120 秒后开始探测,每 30 秒探测一次,连续 3 次无响应就判定连接断开。很多“假死”问题(应用不退出,连接却已失效)用它来解决非常有效。
第五,如果业务对连接时延特别敏感,比如交易系统或工业实时控制,可以结合 TCP_NODELAY(禁用 Nagle 算法)来减少小包的发送延迟。在 Socket 编程中,设置 TCP_NODELAY 后,小数据包会立即发送,不再等待合并。代价是网络包数量会增加,带宽利用率下降,所以要在时延和效率之间做取舍。
我个人的经验是,网络调优从来不是一步到位的事情,必须结合业务特点做 A/B 测试。先把监控数据收集齐,再改一个参数,观察一段时间,确认有效后再继续下一个调整,这才是稳妥的工程做法。
7. 最后的实战建议
TCP、UDP 和端口这些概念看似基础,但真正排查起问题来,现场往往是一团迷雾。一个连接建立不了,可能是防火墙拦截、服务端 backlog 溢出、端口被占、路由不通、对端根本不监听,甚至可能是中间设备做了 NAT 映射导致端口对不上。面对这么多可能性,一定要有一套清晰的排查顺序。
我的习惯是:从链路层到传输层,再从传输层到应用层。先 ping 确认网络通不通,再 nc 或 telnet 确认端口通不通,再 ss 确认服务监听状态,最后抓包看应用层数据是否正确。每一步都用数据说话,不靠猜。
这套方法帮我解决过无数个“看起来很奇怪”的问题。希望这篇文章也能让你在面对 TCP/UDP 和端口相关问题时更有底气。网络排查本来就是一个不断积累的过程,多抓几次包、多看几次状态,很多概念自然就通透了。
