2014-09-28 12 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

相關問題