Handling udp read / write connection in Go

I need to create a UDP connection through which I can write and read packets at the same time. (using different goroutines and with GOMAXPROCS (n), where n> 1) The first attempt was something like this:

func new_conn(port, chan_buf int) (conn *net.UDPConn, inbound chan Packet, err error) { inbound = make(chan Packet, chan_buf) conn, err = net.ListenUDP("udp4", &net.UDPAddr{Port: port}) if err != nil {return} go func () { for { b := make([]byte, UDP_PACKET_SIZE) n, addr, err := conn.ReadFromUDP(b) if err != nil { log.Printf("Error: UDP read error: %v", err) continue } inbound <- Packet{addr, b[:n]} } } } 

So, to read the package, I used the package: = <- inbound and wrote conn.WriteTo (data_bytes, remote_addr) . But the race detector warns of simultaneous read / write when connected. So I rewrite the code like this:

 func new_conn(port, chan_buf int) (inbound, outbound chan Packet, err error) { inbound = make(chan Packet, chan_buf) outbound = make(chan Packet, chan_buf) conn, err = net.ListenUDP("udp4", &net.UDPAddr{Port: port}) if err != nil {return} go func () { for { select { case packet := <- outbound: _, err := conn.WriteToUDP(packet.data, packet.addr) if err != nil { log.Printf("Error: UDP write error: %v", err) continue } default: b := make([]byte, UDP_PACKET_SIZE) n, addr, err := conn.ReadFromUDP(b) if err != nil { log.Printf("Error: UDP read error: %v", err) continue } inbound <- Packet{addr, b[:n]} } } } } 

This code will no longer trigger a race condition, but has the risk of blocking goroutine if there are no incoming packets. The only solution I see is to call something like SetReadDeadline (time.Now () + 10 * time.Millisecond) before calling ReadFromUDP. This code will probably work, but I don't like it. Are there any more elegant ways to solve this problem?

UPD: Warning:

 ================== WARNING: DATA RACE Read by goroutine 553: net.ipToSockaddr() /usr/local/go/src/pkg/net/ipsock_posix.go:150 +0x18a net.(*UDPAddr).sockaddr() /usr/local/go/src/pkg/net/udpsock_posix.go:45 +0xd9 net.(*UDPConn).WriteToUDP() /usr/local/go/src/pkg/net/udpsock_posix.go:123 +0x4df net.(*UDPConn).WriteTo() /usr/local/go/src/pkg/net/udpsock_posix.go:139 +0x2f6 <traceback which points on conn.WriteTo call> Previous write by goroutine 556: syscall.anyToSockaddr() /usr/local/go/src/pkg/syscall/syscall_linux.go:383 +0x336 syscall.Recvfrom() /usr/local/go/src/pkg/syscall/syscall_unix.go:223 +0x15c net.(*netFD).ReadFrom() /usr/local/go/src/pkg/net/fd_unix.go:227 +0x33c net.(*UDPConn).ReadFromUDP() /usr/local/go/src/pkg/net/udpsock_posix.go:67 +0x164 <traceback which points on conn.ReadFromUDP call> Goroutine 553 (running) created at: <traceback> Goroutine 556 (running) created at: <traceback> ================== 
+7
go udp
source share
2 answers

According to the trace from the race detector, the detected race is apparently related to the reuse of the UDPAddr returned by the read call in a subsequent record. In particular, the data referenced by its IP fields.

It is not clear that this is really a problem, since syscall.ReadFrom allocates a new address structure for each call and does not hold on to this structure in the long run. You can try to copy the address before sending it to the outgoing version of goroutine. For example:

 newAddr := new(net.UDPAddr) *newAddr = *addr newAddr.IP = make(net.IP, len(addr.IP)) copy(newAddr.IP, add.IP) 

But without knowing more about your program, it’s hard to say why this is marked as a race. It may be enough to point you in the right direction. I could not reproduce the race using this test program, based on what you posted: http://play.golang.org/p/suDG6hCYYP

+3
source share

Why not start two goroutines, one for writing and one for reading and being full duplex? ie:

 func new_conn(port, chan_buf int) (inbound, outbound chan Packet, err error) { inbound = make(chan Packet, chan_buf) outbound = make(chan Packet, chan_buf) conn, err = net.ListenUDP("udp4", &net.UDPAddr{Port: port}) if err != nil { return } go func() { for packet := range outbound { _, err := conn.WriteToUDP(packet.data, packet.addr) if err != nil { log.Printf("Error: UDP write error: %v", err) continue } } }() go func() { b := make([]byte, UDP_PACKET_SIZE) for { n, addr, err := conn.ReadFromUDP(b) if err != nil { log.Printf("Error: UDP read error: %v", err) continue } b2 := make([]byte, UDP_PACKET_SIZE) copy(b2, b) inbound <- Packet{addr, b2[:n]} } }() } 
+1
source share

All Articles