概述
一 背景
同事说告警报文没办法接收了, 程序同时向本机发送 UDP 告警和另外一台机器发送 UDP 报文,结果显示,本机 UDP 是正常收到的,远程的机器收不到 UDP 报文的.
二 问题分析
2.1 基本分析
程序同时发送到本地应用和远程应用的,虽然是不同的 IP 和端口,但是是同一个逻辑,所以程序的本身的问题可能性比较小,先测试下是否为网络问题:
先用 ping 测试下,由于 ping 被禁止了,所以没有测试出什么来.
利用
nc -ul xxx
在远程机器新建一个 udp 监听端口,应用程序所在机器,通过nc -u x.x.x.x xxx
连接后,通过输入一些字符,来看看远程机器是否有屏显,结果本次测试正常,说明网络是通的.继续在远程的机器抓包,发现不是没有收到,而是收到的包相对来说比较小,大小为 400 多个字节左右,均是这种小于 1 千个字节的.
在发送告警的机器通过 tcpdump 抓包:
tcpdump -i eth1 udp port xxxx -A -nn
发现有类似于以下内容的告警:
21:01:39.000550 IP (tos 0x0, ttl 64, id 63736, offset 0, flags [+], proto UDP (17), length 1500)
xxx.xxx.xxx.xxx.59019 > xxx.xxx.xxx.xxx.documentum: UDP, bad length 6902 > 1472
首先,我们来看下这个 1472 是怎么来的,在以太网环境中,以太网的帧的 body 大小为 46 字节到 1500 字节之间,本次是处于 IPV4 的环境,IP 包头大小为 20 个字节,所以还剩下 1480 字节;UDP 的协议的报文头长度为 8 个字节,所以剩下的 udp 的包体长度为 1480-8 = 1472 个字节,具体展示如下图: 格式如下:
上述告警意思是因为我们环境下网卡的 MTU 设置为 1500 个字节,如下:
[root@localhost ~]# ifconfig
em1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
因为发送的 UDP 报文长度大于可以传输的安全长度 1472 个字节,这不代表不能发送,只是因为大于了帧的最大传输长度,所以在 IP 层需要进行分包,一旦网络环境不好,分包产生了丢失问题,会造成 IP 的组包失败,从而导致 UDP 的报文丢失.
不过鉴于 Internet 上的标准 MTU 值为 576 字节,所以建议在进行 Internet 的 UDP 编程时,最好将 UDP 的数据长度控制在 (576-8-20)548 字节以内
还可以通过netstat -su
进行监控:
[root@localhost ~]# netstat -su
IcmpMsg:
InType0: 141039
InType3: 72945
InType8: 1616
InType13: 1
OutType0: 1616
OutType3: 777474
OutType8: 141039
OutType14: 1
Udp:
4039279 packets received
123325 packets to unknown port received.
67020 packet receive errors
4229636 packets sent
67020 receive buffer errors
0 send buffer errors
UdpLite:
IpExt:
InNoRoutes: 2
InMcastPkts: 26
InBcastPkts: 723113
InOctets: 27500413848629
OutOctets: 27491308862298
InMcastOctets: 832
InBcastOctets: 287040162
InNoECTPkts: 126755848707
InECT0Pkts: 16
2.2 尝试解决
既然 MTU 太小了,那么尝试修改下两端的 MTU 最大值,MTU 是取整个路由的 MTU 最小值,我们尝试把两端的 MTU 增大下:
ifconfig eth1 mtu 9000 up
两端 MTU 增加后,仍然会报错,那么可能的原因是中间路由设备设置的 MTU 比较小,查看下,由于主机上没有 traceroute 命令来跟踪,尝试使用另外一个命令:
tracepath xxx.xxx.xx.xx.xx
类似于 traceroute,可以追踪路由,结束后打印 MTU 值. 还可以带个端口,测试这个 UDP 端口.
[root@localhost ~]# tracepath 1xx.xx.xx.2xx/10001
1?: [LOCALHOST] pmtu 1500
1: xx.xx.xx.xx 0.502ms reached
1: xx.xx.xx.xx 0.323ms reached
Resume: pmtu 1500 hops 1 back 1
在实际环境中,由于中间很多路由都看不到,而且让中间的所有路由都改 MTU 值不是太现实.
在 MTU 为 1500 字节的情况下,如果发送的 UDP 报文大于 MTU,比如发送 8000 个字节,如果包缓存足够,且分包按照正确的顺序到来,通过 recvfrom(9000) 还是可以收到一个完整的 UDP 包的. 如果 IP 分片丢失,校验失败,包就会丢弃.recvfrom(9000)将阻塞.
2.3 更改 socket 缓冲区大小
为防止 socket 缓冲区溢出造成的问题,特意增加了 socket 的缓冲区.cat /proc/sys/net/core/rmem_default
和cat /proc/sys/net/core/rmem_max
可以查看 socket 缓冲区的缺省值和最大值。可以通过echo xxx >/proc/sys/net/core/rmem_default
的方法来临时修改,也通过更改/etc/sysctl.conf 文件添加以下配置来修改:
net.core.rmem_default=262144
net.core.wmem_default=262144
net.core.rmem_max=262144
net.core.wmem_max=262144
修改完成后记得运行以下命令来生效:
sysctl -p
但是在本次仍然没起到效果.
2.4 最终解决方法
最终解决方法是绕过了这个问题,直接改了接口,不采用 UDP 发送了,而是采用文件采集形式. 这是一次不成功的经验,有这方面经验的朋友,可以留言交流下还有什么原因造成这种问题.
三 诗词欣赏
阵雨
夏令欣逢细雨濛,花轩廊下看斜风。
天光忽觉流云散,忙向东边盼彩虹。
夏
雨后偷闲竹椅横,葡萄架下梦初成。
荷风偏扰昏昏睡,偶送鸣蛙四五声。
最后
以上就是机灵板栗为你收集整理的一次UDP收不到问题排查的全部内容,希望文章能够帮你解决一次UDP收不到问题排查所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复