如何在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但这确实很丑陋。我可以设置一个标志,说如果要设置,我将要关闭并忽略错误-有更好的方法吗?


慕斯709654
浏览 276回答 2
2回答

汪汪一只猫

accept()调用后立即在循环中检查一些“是否该停止了”标志,然后从中将其翻转main,然后连接到侦听端口以获取服务器套接字“未卡住”的标志。这与旧的“自管技巧”非常相似。

侃侃无极

我可以通过在关闭连接之前使用es.done发送信号来处理此问题。除了以下代码,您还需要使用make(chan bool,1)创建es.done,以便我们可以在其中添加单个值而不会阻塞。// Listen for incoming connectionsfunc (es *EchoServer) serve() {&nbsp; for {&nbsp; &nbsp; conn, err := es.listen.Accept()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; case <-es.done:&nbsp; &nbsp; &nbsp; &nbsp; // If we called stop() then there will be a value in es.done, so&nbsp; &nbsp; &nbsp; &nbsp; // we'll get here and we can exit without showing the error.&nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; log.Printf("Accept failed: %v", err)&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; }&nbsp; &nbsp; go es.respond(conn.(*net.TCPConn))&nbsp; }}// Stop the server by closing the listening listenfunc (es *EchoServer) stop() {&nbsp; es.done <- true&nbsp; &nbsp;// We can advance past this because we gave it buffer of 1&nbsp; es.listen.Close() // Now it the Accept will have an error above}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go