前面说了几个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那种使用循环去接。