美文网首页网络
【udp】关于 udp socket 连接问题分析

【udp】关于 udp socket 连接问题分析

作者: Bogon | 来源:发表于2022-06-04 00:07 被阅读0次

    一、 udp问题抓包分析

    公司内部的一个 中间件报 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 返回异常错误,如果客户端陷入等待,则唤醒起来,设置错误状态。

    (上面是 udp 异常情况下的 icmp,下面是udp 正常 情况下的icmp)

    image.png image.png

    写UDP socket程序的时候,在调用sendto或者recvfrom的时候,会发现有Connection refused错误返回,错误码是ECONNREFUSED。

    对于懂得socket接口但是不很很懂网络的人,可能这根本就不是个问题,他会根据错误码知道远端没有这个服务端口,正如socket api的man手册中描述的那样:

    ECONNREFUSED
    A remote host refused to allow the network connection (typically because it is not running the requested service).
    

    如果你十分精通TCP/IP栈,那么就想不通了,UDP既然无连接,怎么知道远端的情况呢?
    UDP不正如协议标准描述的那样,发出去就不管了吗?
    对于接收,没有数据就一直等,如果设置了NOWAIT,则直接返回EAGAIN,表示稍后再试。不管怎么说,也不会有ECONNREFUSED这么详细的信息返回才对啊。

    既然UDP不会从对端返回任何错误信息,那么一定有别的什么返回了,这就涉及到了网络协议设计中的数据平面和控制平面了,对于控制平面的消息,可以是带内传输,也可以是带外传输。

    数据分为两种,一种是带内数据,一种是带外数据。
    带内数据就是我们平常传输或者说是口头叫的数据,带外数据就是我们接下来讲的内容。

    许多的传输层都具有带外数据(也称为 经加速数据 )的概念,想法就是连接的某段发生了重要的事情,希望迅速的通知给对端。这里的迅速是指这种通知应该在已经排队了的带内数据之前发送,也就是说,带外数据拥有更高的优先级。带外数据可以使用一条独立的传输层连接,也可以映射到传输普通数据的连接中。其中,UDP没有实现带外数据。

    TCP中telnet、rlogin和ftp等,除了这样的远程非活跃应用之外,几乎很少有使用到带外数据的地方。

    TCP利用其头部中的紧急指针标志以及紧急指针字段,给应用程序提供里一种紧急方式,所以TCP是利用传输普通数据的连接来传输带外数据。

    对于TCP而言,无疑是带内传输的,因为它本身就是有连接的协议,协议本身会处理任何的错误和异常,然而对于UDP而言,因为其设计目的就是保持简单性,故不再附带有任何带内的控制消息逻辑,互联网上为了弥补这一类协议的控制逻辑的缺失,ICMP协议才显得尤为重要!

    实际上,ICMP,根据名称就可以看出它是一种专门的控制协议,控制和指示IP层发生的事件。

    ECONNREFUSED正是ICMP返回的,然而并不是所有的UDP socket都可以享用ICMP带来的错误提示,毕竟带外控制消息和协议本身的关联太松散了。
    UDP socket必须显式的connect对端才可以。

    现在问题又来了,既然UDP根本就是一个无连接的协议,connect的意义何在呢?
    这其实是socket接口设计的范畴,和协议本身没有任何关系,当一个UDP socket去 connect一个远端时,并没有发送任何的数据包,其效果仅仅是在本地建立了一个五元组映射,对应到一个对端,该映射的作用正是为了和UDP带外的ICMP控制通道捆绑在一起,使得UDP socket的接口含义更加丰满。

    image.png

    我们知道,ICMP错误信息返回时,ICMP的包内容就是出错的那个原始数据包,根据这个原始数据包可以找出一个五元组,根据该五元组就可以对应到一个本地的connect过的UDP socket,进而把错误消息传输给该socket,应用程序在调用socket接口函数的时候,就可以得到该错误消息。
    如果一个UDP socket没有调用过connect,那么即使有ICMP数据包返回,由于socket保持了UDP的完整语义,协议栈也就不保存关于该socket和对端关联的任何信息,因此也就无法找到一个特定的五元组将错误码传给它。

    你不能太指望这个Connection refused以及一切带外返回的错误信息,因为你不能保证一定能收到远端发送的ICMP包,如果中间的某个节点或者本机禁掉了ICMP,socket api调用就无法捕获这些错误了。

    当 UDP 连接异常时,可以通过 tcpdmp 工具指定 ICMP 协议来抓取该异常报文,毕竟对方是通过 icmp 返回的 ECONNREFUSED。

    我们使用 tcpdump 抓包,先找到一个可以 ping 通的目标主机, 然后用 nc 模拟 udp 客户端去请求不存在的端口,出现 Connection refused.

    # yum  -y install  nc 
    
    # 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 refused.
    
    # tcpdump   -i   any   icmp   -nn
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
    17:01:14.075617 IP 172.16.0.46 > 172.16.0.62: ICMP 172.16.0.46 udp port 8888 unreachable, length 37
    17:01:17.326145 IP 172.16.0.46 > 172.16.0.62: ICMP 172.16.0.46 udp port 8888 unreachable, length 37
    17:01:17.927480 IP 172.16.0.46 > 172.16.0.62: ICMP 172.16.0.46 udp port 8888 unreachable, length 37
    17:01:18.489560 IP 172.16.0.46 > 172.16.0.62: ICMP 172.16.0.46 udp port 8888 unreachable, length 37
    

    注意: telnet 不支持 udp,,只支持 tcp,建议使用 nc 来探测 udp。

    二、各种case的测试

    case小结:

    1. 当 ip 无法连通时, udp 客户端连接时,通常会显示成功
    2. 当 udp 服务端程序关闭, 但系统还存在时, 对方系统会 icmp ECONNREFUSE 错误
    3. 当对方有操作 iptables udp port drop 时,通常客户端也会显示成功.
    # ping 172.16.0.65
    
    PING 172.16.0.65 (172.16.0.65) 56(84) bytes of data.
    From 172.16.0.46 icmp_seq=1 Destination Host Unreachable
    From 172.16.0.46 icmp_seq=2 Destination Host Unreachable
    From 172.16.0.46 icmp_seq=3 Destination Host Unreachable
    From 172.16.0.46 icmp_seq=4 Destination Host Unreachable
    From 172.16.0.46 icmp_seq=5 Destination Host Unreachable
    From 172.16.0.46 icmp_seq=6 Destination Host Unreachable
    ^C
    --- 172.16.0.65 ping statistics ---
    6 packets transmitted, 0 received, +6 errors, 100% packet loss, time 4999ms
    pipe 4
    
    # nc   -zuv 172.16.0.65 8888
    Ncat: Version 7.50 ( https://nmap.org/ncat )
    Ncat: Connected to 172.16.0.65:8888.
    Ncat: UDP packet sent successfully
    Ncat: 1 bytes sent, 0 bytes received in 2.02 seconds.
    

    再次明确一点 udp 没有类似 tcp 那样的状态报文, 所以单纯对 UDP 抓包是看不到啥异常信息.
    那么当 IP 不通时, 为啥nc udp 命令显示成功 ?

    netcat nc udp 的逻辑

    为什么当 ip 不连通或者报文被 drop 时,返回连接成功 ?

    因为 nc 默认的探测逻辑很简单,只要在 2 秒钟内没有收到 icmp ECONNREFUSED 异常报文, 那么就认为 UDP 连接成功。

    所以, UDP 客户端,给无法连通的地址发 UDP 报文时,其实也不会报错, 这时候通常会认为发送成功。

    还是那句话 UDP 没有 TCP 那样的握手步骤,像 TCP 发送 syn 总得不到回报时, 协议栈会在时间退避下尝试 6 次,当 6 次还得不到回应,内核会给与错误的 errno 值。

    UDP 连接信息

    在客户端的主机上, 通过 ss lsof netstat 可以看到 UDP 五元组连接信息。

    $ netstat  -tunalp | grep 29999
    
    udp   0   0 172.16.0.46:44136  172.16.0.46:29999   ESTABLISHED 1285966/client
    
    

    通常在服务端上看不到 UDP 连接信息, 只可以看到 udp listen 信息 !

    # netstat -tunalp|grep 29999
    
    udp    0      0 :::29999     :::*     4038720/server
    
    

    客户端重新实例化问题 ?
    当 client 跟 server 已连接,server 端手动重启后,客户端无需再次重新实例化连接,可以继续发送数据。当服务端再次启动后,照样可以收到客户端发来的报文。

    udp 本就无握手的过程,他的 udp connect() 也只是在本地创建 socket 信息. 在服务端使用 netstat 是看不到 udp 五元组的 socket。

    image.png image.png

    总结

    当 udp 服务端的机器可以连通且无异常时,客户端通常会显示成功。
    但当有异常时,会有以下的情况:

    1. 当 ip 地址无法连通时, udp 客户端连接时,通常会显示成功

    2. 当 udp 服务端程序关闭, 但系统还存在时, 对方系统通过 icmp ECONNREFUSE 返回错误,客户端会报错

    3. 当对方有操作 iptables udp port drop 时,客户端也会显示成功

    4 客户端和服务端互通数据,当服务进程挂了时,UDP 客户端不能立马感知关闭状态,只有当再次发数据时才会被对方系统回应 icmp ECONNREFUSE 异常报文, 客户端才能感知对方挂了。

    三、参考

    让人迷糊的 socket udp 连接问题
    https://xiaorui.cc/archives/7255

    TCP/IP 某些最常见的错误原因码 (errno)列表
    https://www.cnblogs.com/jiu0821/p/5895723.html

    技术分享之网络编程的那些事儿
    https://xiaorui.cc/archives/7271

    TCP 带外数据(即紧急模式的发送和接受)
    https://blog.csdn.net/liushengxi_root/article/details/82563181

    TCP带外数据
    https://www.cnblogs.com/c-slmax/p/5553857.html

    TCP-带外数据(紧急数据)
    https://www.jianshu.com/p/65a4b8c059d4

    什么是带外管理和带内管理?
    https://zhuanlan.zhihu.com/p/341264872

    FAQ-什么是带外管理和带内管理?它们的区别是什么?
    https://support.huawei.com/enterprise/zh/knowledge/EKB1000055297

    相关文章

      网友评论

        本文标题:【udp】关于 udp socket 连接问题分析

        本文链接:https://www.haomeiwen.com/subject/icghmrtx.html