gpt4 book ai didi

go - 在 Go 中处理读/写 udp 连接

转载 作者:IT王子 更新时间:2023-10-29 01:15:03 30 4
gpt4 key购买 nike

我需要创建 UDP 连接,通过它我可以同时写入和读取数据包。 (使用不同的 goroutines 和 GOMAXPROCS(n) where n>1)第一次尝试是这样的:

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

因此,为了读取数据包,我使用了packet := <- inbound 并写入了conn.WriteTo(data_bytes, remote_addr)。但是race detector在连接上同时读/写时发出警告。所以我将代码重写成这样:

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

此代码将不再触发竞争条件,但如果没有入站数据包,则有阻塞 goroutine 的风险。我看到的唯一解决方案是在调用 ReadFromUDP 之前调用类似 SetReadDeadline(time.Now()+10*time.Millisecond) 的方法。这段代码可能会起作用,但我不太喜欢它。有没有更优雅的方法来解决这个问题?

UPD:警告信息:

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

最佳答案

根据竞争检测器的跟踪,检测到的竞争似乎是由于在后续写入中重用了读取调用返回的 UDPAddr。特别是 IP 字段引用的数据。

目前尚不清楚这是否真的是一个问题,因为 syscall.ReadFrom 会在每次调用时分配一个新的地址结构,并且不会长期保留该结构。您可以尝试在将地址发送到出站 goroutine 之前复制该地址。例如:

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

但是在不了解您的程序的更多信息的情况下,很难说出为什么将其标记为比赛。也许这足以为您指明正确的方向。根据您发布的内容,我无法使用此测试程序重现比赛:http://play.golang.org/p/suDG6hCYYP

关于go - 在 Go 中处理读/写 udp 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21968266/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com