udp recvfrom connection refused 问题

udp recvfrom: connection refused 问题

出错代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
	"net"
)

func main() {
    udp, err := net.DialUDP("udp", nil, &net.UDPAddr{IP: net.ParseIP("192.168.1.2"),Port: 2222})
	if err != nil {
		panic(err)
	}

	_, err = udp.Write([]byte("test"))
	if err != nil {
		panic(err)
	}
	buff := make([]byte, 1024)
	_, _, err = udp.ReadFromUDP(buff)
	if err != nil {
		panic(err)
	}
}

错误信息:

1
2
3
4
5
6
panic: read udp 192.168.1.5:45529->192.168.1.2:2222: recvfrom: connection refused

goroutine 1 [running]:
main.main()
        /root/udp.go:20 +0x177
exit status 2

问题原因

1
2
3
4
5
root@ubuntu:~# tcpdump -i enp1s0 host 192.168.1.2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on enp1s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:36:06.764008 IP 192.168.1.5.60398 > 192.168.1.2.2222: UDP, length 4
10:36:06.764420 IP 192.168.1.2 > 192.168.1.5: ICMP 10.0.0.1 udp port 2222 unreachable, length 40

使用 tcpdump 抓包发现目标主机为给主机回复了一个 ICMP 包,用来指示 2222 端口不可达。

根据《UNIX网络编程卷1:套接字联网API(第3版)》第八章第8.9小结所讲,这个 ICMP 错误被称为异步错误(asynchronous error)。

该错误是由 sendto 引起(udp.Write函数内部实际调用了 unix 的sendto系统接口),但是 sendto 本身却返回成功。这个 ICMP 数据被送入了 UDP 套接字的接收缓冲区,当调用 recvfrom接口时返回错误。因此当程序调用 udp.ReadFromUDP时会抛出错误。

根据错误分析可以发现,如果没有执行sendto操作,那么recvfrom则不会收到错误信息。

Licensed under CC BY-NC-SA 4.0
最后更新于 Nov 14, 2023 22:34 +0800
使用 Hugo 构建
主题 StackJimmy 设计