(A-->B-->A):A经过UDP发送数据给B(A能够是指定目的地,也能够是广播发送消息给B),B收到消息后根据来源地址和端口向A回发消息,就这么简单的一个通迅过程。html
关于golang udp方面的讲解能够参考下https://colobu.com/2016/10/19/Go-UDP-Programming/这篇文件,讲的挺详细。何时udp socket是connected状态,何时是unconnected状态,何时用read/write,何时用readFromUDP/writeToUDP都有说明,写的挺好。golang
服务端:编程
func main() { // 建立监听 sock, err := net.ListenUDP("udp4", &net.UDPAddr{ IP: net.IPv4(0, 0, 0, 0), Port: 9000, }) if err != nil { fmt.Println("listen udp failure!", err) return } defer sock.Close() for { // 读取数据 data := make([]byte, 4096) readNum, rAddr, err := sock.ReadFromUDP(data) if err != nil { fmt.Println("read data failure!", err) continue } fmt.Println("read byte number:",readNum, "remote addr:", rAddr) fmt.Println("received: ", string(data[:readNum])) // 发送数据 sendBts := []byte("hello client!") _, err = sock.WriteToUDP(sendBts, rAddr) if err != nil { fmt.Println("send msg back failure!", err) return } else { fmt.Println("send msg mack ok.", string(sendBts), "to:", rAddr) } } }
客户端socket
func main() { sock, err := net.DialUDP("udp", nil, &net.UDPAddr{ IP: net.IPv4(255, 255, 255, 255), // 广播地址 Port: 9000, }) // 建立监听 //soct, err := net.ListenUDP("udp4", &net.UDPAddr{ // IP: net.IPv4(0, 0, 0, 0), // Port: 9001, //}) if err != nil { fmt.Println("connect udp failure!", err) return } defer sock.Close() // 发送数据 sendBts := []byte("hello server!") _, err = sock.Write(sendBts) if err != nil { fmt.Println("send msg failure!", err) return } // 接收数据 data := make([]byte, 4096) for { readNum, rAddr, err := sock.ReadFromUDP(data) if err != nil { fmt.Println("read udp failure!", err) return } fmt.Println("read byte number:", readNum, "from:", rAddr) fmt.Println("received: ", string(data[:readNum])) } }
运行下上面的demo:
服务端测试
read byte number: 13 remote addr: 10.200.2.50:54404 received: hello server! send msg mack ok. hello client! to: 10.200.2.50:54404
客户端spa
// nothing
发现客户端啥都没输出,可见我们遇到问题了。服务端明明发送数据了,并且也指定了目的ip和端口号了!为何客户端没收到呢?客户端dial的时候,发广播消息不会帧听本地端口??或者说广播消息的时候,侦听的不是同一个网卡?带着问题再来改下客户端的代码。code
// 启用客户端注释掉的那段代码 func main() { // 建立监听 sock, err := net.ListenUDP("udp4", &net.UDPAddr{ IP: net.IPv4(0, 0, 0, 0), Port: 9001, // 再同一台主机上测试,要跟服务端绑定不一样的端口;若是是不一样的主机,能够用相同的端口 }) if err != nil { fmt.Println("connect udp failure!", err) return } defer sock.Close() // 发送数据 sendBts := []byte("hello server!") _, err = sock.WriteToUDP(sendBts, &net.UDPAddr{ IP: net.IPv4(255, 255, 255, 255), Port: 9000, }) if err != nil { fmt.Println("send msg failure!", err) return } // 接收数据 data := make([]byte, 4096) for { readNum, rAddr, err := sock.ReadFromUDP(data) if err != nil { fmt.Println("read udp failure!", err) return } fmt.Println("read byte number:", readNum, "from:", rAddr) fmt.Println("received: ", string(data[:readNum])) } }
再次运行下
服务端server
read byte number: 13 remote addr: 10.200.2.50:54404 received: hello server! send msg mack ok. hello client! to: 10.200.2.50:54404
客户端htm
received: hello client!
看结果还真印证了没有侦听就不能收消息的猜想。为何dial的时候没有侦听呢?记得c代码会隐式的绑定(参考https://stackoverflow.com/questions/54443823/udp-socket-sendto-implicit-bind,https://www.cnblogs.com/skyfsm/p/6287787.html),语言之间会有不一样的封装吗?我们就一同来看看golang的源码。
看到dialUDP和listenUDP这两个方法,也就红框里的参数不同,其余能够说都同样。再继续往下看internetSocket -> socket方法
注意到红框里都这段代码,它是给udp侦听/绑定用的,可是有个前提:要存在local addr(laddr)并且要不存在remote addr(raddr)。blog
那再改下客户端DialUDP的代码
sock, err := net.DialUDP("udp", &net.UDPAddr{ IP: net.IPv4(0,0,0,0), Port: 9001, }, nil)
再次运行客户端的代码。
connect udp failure! dial udp 0.0.0.0:9001: missing address
获得了一个缺乏地址的一个错误,那说明DialUDP是不支持隐式绑定的。
在使用udp编程的时候呢,若是是单纯的发送数据,可使用DialUDP来得到socket句柄,而后调用write方法发送数据。若是想要收消息无论是客户端仍是服务端,都得用ListenUDP,侦听/绑定后才能够接收消息。