sockets - socket - ¿Cómo detengo un servidor de escucha en Go?
socket io server (4)
He estado tratando de encontrar una manera de detener un servidor de escucha en Go graceful. Porque listen.Accept
bloques, es necesario cerrar el zócalo de escucha para indicar el final, pero no puedo distinguir ese error de otros errores, ya que el error relevante no se exporta.
¿Puedo hacerlo mejor que esto? Ver FIXME
en el siguiente código en serve()
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()
}
Esto imprime
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
Me gustaría ocultar el mensaje Accept failed
, pero obviamente no quiero enmascarar otros errores que Accept
pueda informar. Por supuesto, podría ver la prueba de error para el use of closed network connection
pero eso sería realmente feo. Podría establecer una bandera que diga que estoy a punto de cerrar e ignorar los errores si eso se hubiera establecido, supongo. ¿Hay alguna forma mejor?
Algo entre estas líneas podría funcionar en este caso, espero:
// 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
}
Aquí hay una manera simple que es lo suficientemente buena para el desarrollo local.
http://www.sergiotapia.me/how-to-stop-your-go-http-server/
package main
import (
"net/http"
"os"
"github.com/bmizerany/pat"
)
var mux = pat.New()
func main() {
mux.Get("/kill", http.HandlerFunc(kill))
http.Handle("/", mux)
http.ListenAndServe(":8080", nil)
}
func kill(w http.ResponseWriter, r *http.Request) {
os.Exit(0)
}
Manejaría esto usando es.done para enviar una señal antes de que cierre la conexión. Además del siguiente código, necesitaría crear es.done con make (chan bool, 1) para que podamos ponerle un único valor sin bloquear.
// 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
}
Verifique el indicador "es el momento de parar" en su bucle justo después de la llamada de accept()
, luego páselo de la red main
, luego conéctese a su puerto de escucha para que el socket del servidor se "desbloquee". Esto es muy similar al antiguo "truco de auto-pipa" .