I need to read UDP traffic until a timeout is reached. I can do this by calling SetDeadline on the UDPConn and looping until I get an I/O timeout error, but this seems hack-ish (flow control based on error conditions). The following code snippet seems more correct, but does not terminate. In production, this would obviously be executed in a goroutine; it's written as a main function for simplicity's sake.
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
}
}
}
Why doesn't the given program terminate? Based on https://gobyexample.com/select, https://gobyexample.com/timeouts, and https://gobyexample.com/non-blocking-channel-operations, I would expect the above code to select the default case for one second, then take the first case and break out of the loop. How might I modify the above snippet to achieve the desired effect of looping and reading until a timeout occurs?
Simply assign the channel from
time.After
outside thefor
loop, otherwise you will just create a new timer each time you loop.Example:
Note that this doesn't work on the playground.
If you are not concerned about read blocking past
n
seconds, then loop until the deadline:If you do want to break out of a blocking read after
n
seconds, then set a deadline and read until there's an error:Using a deadline is not hacky. The standard library uses deadlines while reading UDP connections (see the dns client).
There are alternatives to using a deadline to break a blocking read: close the connection or send a dummy packet that the reader recognizes. These alternatives require starting another goroutine and are much more complicated than setting a deadline.