2014-09-28 69 views
1

我需要读取UDP流量,直到达到超时。我可以通过调用UDPConn上的SetDeadline并循环,直到出现I/O超时错误,但这似乎是hack-ish(基于错误条件的流量控制)。下面的代码片段似乎更正确,但不会终止。在生产中,这显然将在一个公用事业中执行;为了简单起见,它被写为主要功能。如何在达到超时之前读取UDP连接?

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/selecthttps://gobyexample.com/timeoutshttps://gobyexample.com/non-blocking-channel-operations,我期望上面的代码选择默认情况下一秒钟,然后采取第一种情况并跳出循环。如何修改上面的代码片段以达到循环和读取所需的效果,直到发生超时?

+0

如果您将'break'更改为'return',则该功能将在持续时间内完成。您目前正在循环使用,并且每秒都会打印完成。 – cdbitesky 2014-09-28 02:36:58

+0

@chendesheng我相信时间的使用。以后是惯用的(请参阅http://golang.org/pkg/time/#example_After) 感谢你们两人对break语句的理解;我更新了片段。该程序仍然没有终止,但 – cqcallaw 2014-09-28 02:43:55

+0

@chendesheng我忘了提及:我曾尝试使用time.Tick来代替,但仍然有无限循环行为 – cqcallaw 2014-09-28 02:55:29

回答

2

只需从循环外的time.After分配通道,否则每次循环时都会创建一个新的计时器。

例子:

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

注意,这并不在操场上工作。

+0

这个工程!但为什么这是必要的?是时候了。表达式被评估为循环的每一次迭代? – cqcallaw 2014-09-28 17:20:22

+1

@cqcallaw是的,你正在每个循环创建一个新的计时器。 – OneOfOne 2014-09-28 17:21:19

+0

有趣。为什么此代码按预期运行,然后呢?请注意在该示例中的“case <-time.After(time.Second * 1)”https://gobyexample.com/timeouts – cqcallaw 2014-09-28 17:24:11

4

如果你不关心阅读堵过去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 
} 

使用截止日期并不是一件坏事。标准库在读取UDP连接时使用最终期限(请参阅dns client)。

还有其他方法可以使用截止日期来中断阻塞读取:关闭连接或发送读取器识别的虚拟数据包。这些替代方案需要启动另一个goroutine,并且比设定最后期限要复杂得多。

+0

Upvoted用于特定于问题的知识。你有没有在标准库中使用截止日期方法的例子? – cqcallaw 2014-09-28 17:21:09

+1

我希望能够将多个答案标记为回答问题!我觉得OneOfOne的答案仍然能够最好地回答问题,但您的答案在实践中更有用。感谢您提供具体问题的具体信息。 – cqcallaw 2014-10-01 15:47:44

相关问题