Как остановить сервер прослушивания в Go
Я пытаюсь найти способ остановить прослушивающий сервер в Go изящно. Поскольку блоки listen.Accept
необходимо закрыть слушающий сокет, чтобы сигнализировать о конце, но я не могу сказать эту ошибку, кроме любых других ошибок, поскольку соответствующая ошибка не экспортируется.
Могу ли я сделать лучше, чем это? См. FIXME
в приведенном ниже коде в 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()
}
Отпечатает
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
Я хотел бы скрыть сообщение Accept failed
, но, очевидно, я не хочу маскировать другие ошибки, о которых может сообщить Accept
. Конечно, я мог бы проверить тест на ошибку use of closed network connection
, но это было бы очень уродливо. Я могу установить флаг, говорящий, что я собираюсь закрыть и игнорировать ошибки, если это было установлено. Я полагаю, что есть лучший способ?
Ответы
Ответ 1
Отметьте флажок "время останавливаться" в вашем цикле сразу после вызова accept()
, а затем переверните его со своего main
, затем подключитесь к порту прослушивания, чтобы получить "сокет" сервера. Это очень похоже на старый "трюк с собственной трубкой" .
Ответ 2
Я бы справился с этим, используя es.done для отправки сигнала, прежде чем он закроет соединение. В дополнение к следующему коду вам нужно будет создать es.done с make (chan bool, 1), чтобы мы могли поместить в него одно значение без блокировки.
// 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
}
Ответ 3
Что-то из этих строк может работать в этом случае, я надеюсь:
// 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
}
Ответ 4
Вот простой способ, который достаточно хорош для локального развития.
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)
}