前面說了幾個tcp模式下的socket新手易錯點,今天就來看看udp的新手易錯點。
先讓我們看一段代碼:
l, e := net.ListenPacket("udp", ":9090")
if e != nil {
fmt.Println(e)
}
b := make([]byte, 10)
for {
n,a, e := l.ReadFrom(b)
if n == 0 || e == io.EOF {
break
}
fmt.Println(n,a)
}
fmt.Println(string(b))
與dial不同,go對tcp和udp分別封裝進了net.Listen()和net.ListenPacket(),具體關係大家可以看: net包 listen - golang
在這篇裏有畫一個簡單關係圖。
上面代碼,是我們仿照tcp模式的sokcet寫的一個例子,那這個例子是不是會和當初tcp一樣執行正確呢。讓我們看看輸出:
在這裏我們發送的還是tcp中提到的(我是tcp socket測試),如果你自己執行,你會發現udp情況下Read一次後程序就陷入了阻塞,而不像tcp那樣讀完數據後才進入阻塞。
之所以有這種不同變化是因爲他們不同的發送形式,tcp一般以流的形式發送,而udp一般是以數據報的形式發送的。所以他在讀取一次後即使沒有讀完,程序也會把後續數據丟棄。具體可以看sock_posix.go這個文件的socket函數:
if laddr != nil && raddr == nil {
switch sotype {
case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET:
if err := fd.listenStream(laddr, listenerBacklog(), ctrlFn); err != nil {
fd.Close()
return nil, err
}
return fd, nil
case syscall.SOCK_DGRAM:
if err := fd.listenDatagram(laddr, ctrlFn); err != nil {
fd.Close()
return nil, err
}
return fd, nil
}
}
所以我們在取數據的時候,應該商定好數據報的長度,或者用一個大的byte數組去接,然後截取。而不應該想tcp那種使用循環去接。