duanjianqu3685 2014-02-23 12:36
浏览 54
已采纳

在Go中处理读/写udp连接

I need to create UDP connection through which I could write and read packets simultaneously. (using different goroutines and with GOMAXPROCS(n) where n>1) 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 packet I used packet := <- inbound and to write conn.WriteTo(data_bytes, remote_addr). But race detector issues warnings on simultaneous read/write on connection. So I rewrite code to something 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 more trigger race condition, but have risk of blocking goroutine if there is no inbound packets. Only solution which 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 so much. Is there more elegant ways to solve this problem?

UPD: Warning message:

==================
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>
==================
  • 写回答

2条回答 默认 最新

  • doupi6737 2014-02-24 00:04
    关注

    According to the trace from the race detector, the detected race appears to be due to the reuse of a UDPAddr returned by a read call in a subsequent write. In particular, the data its IP field references.

    It's not clear that this is really a problem though, since syscall.ReadFrom is allocating a new address structure on every call and doesn't hold on to that structure long term. You could try copying the address prior to sending it to your outbound 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 is difficult to tell why this is being flagged as a race. Perhaps it is enough to point you in the right direction though. I wasn't able to reproduce the race using this test program based on what you've posted: http://play.golang.org/p/suDG6hCYYP

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?