如何关闭来自另一个 goroutine 的读取 TCP 连接?

我的服务器 senario 是这样的:一个 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!

    }

}

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


九州编程
浏览 117回答 3
3回答

拉莫斯之舞

选项是关闭连接或将读取截止日期设置为过去的某个时间。无论哪种方式,读取连接都会返回错误。处理这些错误的最简单方法是统一处理网络连接上读取返回的所有错误:关闭连接,清理与连接关联的资源,然后继续。关闭连接两次就可以了。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()    }}// mainconn, 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()    }}}应用程序可以记录它正在关闭连接并在该点之后以特殊方式处理读取错误,但只有在这种情况下应用程序需要做一些特殊的事情时才这样做。

慕沐林林

有一些错误建议单独处理,例如:EOF:一条长收到的消息已经读完,一切正常,继续。“一个现有的连接被远程主机强行关闭”:客户端关闭应用程序。这是正常的,通话结束,所以返回。else: 一些 loginc 或服务器错误,需要记录并修复handler(c net.Conn){&nbsp; &nbsp; defer c.Close()&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp;...&nbsp; &nbsp; &nbsp; &nbsp;if e!=nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if e == io.EOF{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if strings.Contains(e.Error(), "An existing connection was forcibly closed by the remote host.") {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Println(e.Error())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}在你的情况下,你不想处理包含“使用关闭的网络连接”的错误。可以忽略它并记住关闭循环。否则一些内存会泄漏并且例程挂起。可能有一个隐藏的你忽略一遍又一遍地抛出的错误。

梦里花落0921

func recv(c *net.Conn) {&nbsp; &nbsp; if c == nil {&nbsp; &nbsp; &nbsp; &nbsp;return&nbsp; &nbsp; }&nbsp; &nbsp; input := bufio.NewScanner(*c)&nbsp; &nbsp; for input.Scan() {&nbsp; &nbsp; &nbsp; &nbsp; msg <- input.Text()&nbsp; &nbsp; &nbsp; &nbsp; ...&nbsp; &nbsp; }}//mainconn, err := s.Accept()c := &conn...go recv(&c)for {&nbsp; &nbsp; select {&nbsp; &nbsp; case m := <-msg:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;...&nbsp; &nbsp; case <-timeout:&nbsp; &nbsp; &nbsp; &nbsp; conn.Close()&nbsp; &nbsp; &nbsp; &nbsp; c = nil&nbsp; &nbsp; }}不确定这是否是最好的方法。当您关闭 conn 时,您可以将其设置为 nil,而不是从 nil conn 值中读取。
打开App,查看更多内容
随时随地看视频慕课网APP