How do I read a UDP connection until a timeout is reached?

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 that seems like a hack-ish (error condition based flow control). The following code snippet seems more correct, but it doesn't end there. In production this will obviously be done in the larynx; it is written as the main function for simplicity.

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 this program end? 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 case by default for one second, then take the first case and exit the loop. How can I modify the above snippet to achieve the desired loop and read effect until a timeout occurs?

+1


source to share


2 answers


Just assign the channel from time.After

outside the loop for

, otherwise you will just create a new timer every time you loop.

Example:



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

      

Please note that this does not work in the playground.

+3


source


If you are not worried about the past n

seconds reading blocking , then loop to the deadline:

 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")

      

If you want to exit the lock after n

seconds, then set a deadline and read before the error appears:



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 not a timeout
       }
       break
   }
   // do something with packet here
}

      

Using the deadline is not a hack. The standard library uses deadlines when reading UDP connections (see dns client ).

There are alternatives to using a deadline to abort a read lock: close the connection, or send a dummy packet that the reader recognizes. These alternatives require running a different goroutine and are much more difficult than setting a deadline.

+6


source







All Articles