我需要读取UDP流量,直到达到超时。我可以通过在UDPConn上调用SetDeadline并循环直到我得到I / O超时错误来做到这一点,但这似乎是hack-ish(基于错误条件的流量控制)。以下代码段似乎更正确,但不会终止。在生产中,这显然会在goroutine中执行;为了简单起见,它是作为主要功能而写的。
package main
import (
"fmt"
"time"
)
func main() {
for {
select {
case <-time.After(time.Second * 1):
fmt.Printf("Finished listening.\n")
return
default:
fmt.Printf("Listening...\n")
//read from UDPConn here
}
}
}
为什么给定的程序没有终止?基于https://gobyexample.com/select,https://gobyexample.com/timeouts和https://gobyexample.com/non-blocking-channel-operations,我希望上面的代码选择默认情况一秒钟,然后采取第一种情况并突破循环。我如何修改上面的代码段以达到循环和读取所需的效果,直到发生超时?
答案 0 :(得分:8)
如果您不关心超过n
秒的读取阻止,请循环到截止日期:
deadline := time.Now().Add(n * time.Second)
for time.Now().Before(deadline) {
fmt.Printf("Listening...\n")
//read from UDPConn here
}
fmt.Printf("Finished listening.\n")
如果你想在n
秒之后打破阻塞读取,那么设置一个截止日期并阅读,直到出现错误:
conn.SetReadDeadline(time.Now().Add(n * time.Second)
for {
n, err := conn.Read(buf)
if err != nil {
if e, ok := err.(net.Error); !ok || !e.Timeout() {
// handle error, it's not a timeout
}
break
}
// do something with packet here
}
使用截止日期不是hacky。标准库在读取UDP连接时使用截止时间(请参阅dns client)。
使用截止日期来打破阻塞读取还有其他选择:关闭连接或发送读取器识别的虚拟数据包。这些替代方案需要启动另一个goroutine,并且比设定截止日期要复杂得多。
答案 1 :(得分:3)
只需在time.After
循环之外的for
分配频道,否则每次循环时都会创建一个新的定时器。
示例:
func main() {
ch := time.After(time.Second * 1)
L:
for {
select {
case <-ch:
fmt.Printf("Finished listening.\n")
break L // have to use a label or it will just break select
default:
fmt.Printf("Listening...\n")
//read from UDPConn here
}
}
}
请注意,这不适用于操场。