How do I stop a Listening server in Go
Solution 1
Check some "is it time to stop" flag in your loop right after the accept()
call, then flip it from your main
, then connect to your listening port to get server socket "un-stuck". This is very similar to the old "self-pipe trick".
Solution 2
I would handle this by using es.done to send a signal before it closes the connection. In addition to the following code you'd need to create es.done with make(chan bool, 1) so that we can put a single value in it without blocking.
// Listen for incoming connections
func (es *EchoServer) serve() {
for {
conn, err := es.listen.Accept()
if err != nil {
select {
case <-es.done:
// If we called stop() then there will be a value in es.done, so
// we'll get here and we can exit without showing the error.
default:
log.Printf("Accept failed: %v", err)
}
return
}
go es.respond(conn.(*net.TCPConn))
}
}
// Stop the server by closing the listening listen
func (es *EchoServer) stop() {
es.done <- true // We can advance past this because we gave it buffer of 1
es.listen.Close() // Now it the Accept will have an error above
}
Solution 3
Something among these lines might work in this case, I hope:
// Listen for incoming connections
func (es *EchoServer) serve() {
for {
conn, err := es.listen.Accept()
if err != nil {
if x, ok := err.(*net.OpError); ok && x.Op == "accept" { // We're done
log.Print("Stoping")
break
}
log.Printf("Accept failed: %v", err)
continue
}
go es.respond(conn.(*net.TCPConn))
}
es.done <- true
}
Related videos on Youtube
Nick Craig-Wood
Just discovered what is supposed to be in that grey box in my user page ;-) For more about me see My web page My twitter account My Git hub account ...and no that isn't really my birthday, but it is the birth date of something important!
Updated on June 12, 2022Comments
-
Nick Craig-Wood almost 2 years
I've been trying to find a way to stop a listening server in Go gracefully. Because
listen.Accept
blocks it is necessary to close the listening socket to signal the end, but I can't tell that error apart from any other errors as the relevant error isn't exported.Can I do better than this? See
FIXME
in the code below inserve()
package main import ( "io" "log" "net" "time" ) // Echo server struct type EchoServer struct { listen net.Listener done chan bool } // Respond to incoming connection // // Write the address connected to then echo func (es *EchoServer) respond(remote *net.TCPConn) { defer remote.Close() _, err := io.Copy(remote, remote) if err != nil { log.Printf("Error: %s", err) } } // Listen for incoming connections func (es *EchoServer) serve() { for { conn, err := es.listen.Accept() // FIXME I'd like to detect "use of closed network connection" here // FIXME but it isn't exported from net if err != nil { log.Printf("Accept failed: %v", err) break } go es.respond(conn.(*net.TCPConn)) } es.done <- true } // Stop the server by closing the listening listen func (es *EchoServer) stop() { es.listen.Close() <-es.done } // Make a new echo server func NewEchoServer(address string) *EchoServer { listen, err := net.Listen("tcp", address) if err != nil { log.Fatalf("Failed to open listening socket: %s", err) } es := &EchoServer{ listen: listen, done: make(chan bool), } go es.serve() return es } // Main func main() { log.Println("Starting echo server") es := NewEchoServer("127.0.0.1:18081") // Run the server for 1 second time.Sleep(1 * time.Second) // Close the server log.Println("Stopping echo server") es.stop() }
This prints
2012/11/16 12:53:35 Starting echo server 2012/11/16 12:53:36 Stopping echo server 2012/11/16 12:53:36 Accept failed: accept tcp 127.0.0.1:18081: use of closed network connection
I'd like to hide the
Accept failed
message, but obviously I don't want to mask other errorsAccept
can report. I could of course look in the error test foruse of closed network connection
but that would be really ugly. I could set a flag saying I'm about to close and ignore errors if that was set I suppose - Is there a better way?-
Karimai about 5 yearsI believe it is a good explanation. forum.golangbridge.org/t/correct-shutdown-of-net-listener/87ββ05
-
-
Nick Craig-Wood over 11 yearsA good idea thanks! I'm not sure it would tell apart an official stop from
Syscall.Accept()
returning an error (a recent example I've seen is the process running out of sockets) would it? -
Nick Craig-Wood over 11 yearsThat is a neat idea! There is a race condition with real connections coming in though which you'd need to work around.
-
zzzz over 11 yearsDunno, I would have to try/experiment with it.
-
Nikolai Fetissov over 11 yearsYes, true. You can try an inverse of that - always have that single internal connection from the start and use it as a control channel.
-
Nick Craig-Wood over 11 yearsHmm, nice idea. I think a
bool
would do just as well as using the channel though which is pretty much the solution I came up with. You still need the channel though to synchronise thestop
withserve
so you know when it has stopped. -
Dustin over 11 yearsDon't bother buffering the channel or sending a message down the channel. Just close it.
-
Setomidor about 8 yearsExiting the entire process is not a valid way to close a server!
-
luben over 4 yearsGreat example, but shouldn't the
x.Op
be compared toclose
, e.g.&& x.Op == "close"
If we stop the listener by usinglistener.Close()
, it seems to put close inside the error operation. godoc.org/net#TCPListener.Close -
Prisacari Dmitrii over 2 yearsGreat and simple!