首页 > 解决方案 > 如何关闭来自另一个 goroutine 的读取 TCP 连接?

问题描述

我的服务器场景是这样的:一个 io 线程一直在 tcp 连接上进行读取。一段时间后,控制线程可能会由于活动量低或其他原因决定关闭它。如果调用c.Close(),io线程会报如下错误:read tcp xxx->xxx: use of closed network connection

代码是这样的:

func recv(c net.Conn) {
    input := bufio.NewScanner(c)
    for input.Scan() {
        msg <- input.Text()
        ...
    }
}

//main
conn, err := s.Accept()
...
go recv(conn)
for {
    select {
    case m := <-msg:
         ...
    case <-timeout:
        conn.Close() // oops!
    }
}

我也许可以忽略这个错误,但我想知道是否有更好的方法。

标签: go

解决方案


选项是关闭连接或将读取截止日期设置为过去的时间。无论哪种方式,读取连接都会返回错误。

处理这些错误的最简单方法是统一处理网络连接上读取返回的所有错误:关闭连接,清理与连接相关的资源并继续。关闭连接两次就可以了。

func recv(c net.Conn, msg chan string) {
    defer close(msg) // Notify main goroutine that recv is done.
    defer c.Close()  // Release resources.

    input := bufio.NewScanner(c)

    // Loop reading lines until read on c returns an error.
    // These errors include io.EOF (normal close by the peer),
    // the error caused by the main goroutine closing the connection
    // and other networking errors.
    for input.Scan() {
        msg <- input.Text()
    }
}

// main

conn, err := s.Accept()
if err != nil {
    // handle error
}

msg := make(chan string)
go recv(conn, msg)

for {
    select {
    case m, ok := <-msg:
        if !ok {
            // The recv goroutine closed the channel and the connection.
            return
        }
        fmt.Println(m)
    case <-timeout:
        // Closing the connection causes the recv goroutine to break
        // out of the loop. If the recv goroutine received a line of 
        // text and has yet sent the text to the msg channel, then 
        // a return from main at this point will cause the recv goroutine
        // to block forever. To avoid this, continue looping until until
        // the recv goroutine signals that it's done by closing the msg
        // channel.
        conn.Close()
    }
}

}

应用程序可以记录它正在关闭连接并在此之后以特殊方式处理读取错误,但只有在应用程序需要在这种情况下做一些特殊的事情时才这样做。


推荐阅读