Подтвердить что ты не робот

Как остановить сервер прослушивания в 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, но это было бы очень уродливо. Я могу установить флаг, говорящий, что я собираюсь закрыть и игнорировать ошибки, если это было установлено. Я полагаю, что есть лучший способ?

4b9b3361

Ответ 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)
}