公司内部的一个 golang 中间件报 UDP 连接异常的日志,问题很明显,对端的服务挂了,自然重启下就可以了。
哈哈,但让我疑惑的问题是 udp 是如何检测对端挂了?
err: write udp 172.16.44.62:62651->172.16.0.46:29999: write: connection refused
err: write udp 172.16.44.62:62651->172.16.0.46:29999: write: connection refused
err: write udp 172.16.44.62:62651->172.16.0.46:29999: write: connection refused
...
UDP 协议既没有三次握手,又没有 TCP 那样的状态控制报文,那么如何判定对端的 UDP 端口是否已打开?
通过抓包可以发现,当服务端的端口没有打开时,服务端的系统向客户端返回 icmp ECONNREFUSED
报文,表明该连接异常。
通过抓包可以发现返回的协议为 ICMP
,但含有源端口和目的端口,客户端系统解析该报文时,通过五元组找到对应的 socket,并 errno 返回异常错误,如果客户端陷入等待,则唤醒起来,设置错误状态.
![70ccc72b55e77675cd60f036b98081d1.png](https://i-blog.csdnimg.cn/blog_migrate/13f74f31e251586bf0a67e92d04f7b25.png)
(上面是 udp 异常下的 icmp,下面是正常 icmp)
![38bf0be592fdf4b490cd3e2d57ec82c4.png](https://i-blog.csdnimg.cn/blog_migrate/3a48c02929fc3dd6fc630de00a305bd5.png)
当 UDP 连接异常时,可以通过 tcpdump 工具指定 ICMP 协议来抓取该异常报文,毕竟对方是通过 icmp 返回的 ECONNREFUSED。
使用 tcpdump 抓包
请求命令:
先找到一个可以 ping 通的主机,然后用 nc 模拟 udp 客户端去请求不存在的端口,出现 Connection refused
。
[root@ocean ~]# nc -vzu 172.16.0.46 8888
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 172.16.0.46:8888.
Ncat: Connection r