(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,偵聽/綁定後才能夠接收消息。