librerias lib golang go udp

librerias de golang



Manejo de lectura/escritura de la conexión udp en Go (2)

¿Por qué no iniciar dos goroutines, uno para escribir y otro para leer y ser dúplex completo? es decir:

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]} } }() }

Necesito crear una conexión UDP a través de la cual pueda escribir y leer paquetes simultáneamente. (usando diferentes goroutines y con GOMAXPROCS (n) donde n> 1) Primer intento fue algo como esto:

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]} } } }

Entonces para leer el paquete utilicé el paquete: = <- entrante y para escribir conn.WriteTo (data_bytes, remote_addr) . Pero el detector de carreras emite advertencias en la conexión simultánea de lectura / escritura. Así que reescribí el código a algo como esto:

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]} } } } }

Este código ya no activará la condición de carrera, pero tiene el riesgo de bloquear la rutina si no hay paquetes entrantes. La única solución que veo es llamar a algo como SetReadDeadline (time.Now () + 10 * time.Millisecond) antes de llamar a ReadFromUDP. Este código probablemente funcionará, pero no me gusta tanto. ¿Hay formas más elegantes de resolver este problema?

UPD: mensaje de advertencia:

================== 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> ==================


De acuerdo con la huella del detector de raza, la raza detectada parece deberse a la reutilización de un UDPAddr devuelto por una llamada de lectura en una escritura posterior. En particular, los datos a los que su campo IP referencia.

Sin embargo, no está claro que esto sea realmente un problema, ya que syscall.ReadFrom está asignando una nueva estructura de direcciones en cada llamada y no mantiene esa estructura a largo plazo. Puedes intentar copiar la dirección antes de enviarla a tu goroutine de salida. Por ejemplo:

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

Pero sin saber más acerca de su programa, es difícil decir por qué esto se marca como una raza. Sin embargo, tal vez sea suficiente para apuntarle en la dirección correcta. No pude reproducir la carrera usando este programa de prueba según lo que has publicado: http://play.golang.org/p/suDG6hCYYP