首页 > 解决方案 > 关闭从 TCP 连接读取的 goroutine 而不关闭连接

问题描述

我喜欢 Go 在内部处理 I/O 多路复用的方式,epoll以及另一种机制并自行安排绿色线程(此处为 go-routine),从而可以自由地编写同步代码。

我知道 TCP 套接字non-blockingreadEAGAIN没有数据可用时提供。鉴于此,conn.Read(buffer)将检测到这一点并阻止执行连接读取的 go 例程,而套接字缓冲区中没有可用的数据。有没有办法在不关闭底层连接的情况下停止这种 go​​routine。我正在使用连接池,因此关闭 TCP 连接对我来说没有意义,并且希望将该连接返回到池中。

这是模拟这种情况的代码:

func main() {
    conn, _ := net.Dial("tcp", "127.0.0.1:9090")
    // Spawning a go routine
    go func(conn net.Conn) {
        var message bytes.Buffer
        for {
            k := make([]byte, 255) // buffer
            m, err := conn.Read(k) // blocks here 
            if err != nil {
                if err != io.EOF {
                    fmt.Println("Read error : ", err)
                } else {
                    fmt.Println("End of the file")
                }
                break // terminate loop if error
            }
            // converting bytes to string for printing
            if m > 0 {
                for _, b := range k {
                    message.WriteByte(b)
                }
                fmt.Println(message.String())
            }

        }
    }(conn)

    // prevent main from exiting
    select {}
}

如果不可能,我可以采取哪些其他方法:

syscall.Read1)手动调用和处理。在这种情况下,我需要一种方法在调用之前检查套接字是否可读syscall.Read,否则我最终会浪费不必要的 CPU 周期。对于我的场景,我认为我可以跳过基于事件的轮询事情并继续调用syscall.Read,因为我的用例中总是有数据。

2)任何建议:)

标签: socketsgononblockingchannelgoroutine

解决方案


func receive(conn net.TCPConn, kill <-chan struct{}) error {
    // Spawn a goroutine to read from the connection.
    data := make(chan []byte)
    readErr := make(chan error)
    go func() {
        for {
            b := make([]byte, 255)
            _, err := conn.Read(b)
            if err != nil {
                readErr <- err
                break
            }
            data <- b
        }
    }()


    for {
        select {
        case b := <-data:
            // Do something with `b`.
        case err := <-readErr:
            // Handle the error.
            return err
        case <-kill:
            // Received kill signal, returning without closing the connection.
            return nil
        }
    }
}

从另一个 goroutine发送一个空结构体以kill停止从连接接收。这是一个在一秒钟后停止接收的程序:

kill := make(chan struct{})
go func() {
    if err := receive(conn, kill); err != nil {
        log.Fatal(err)
    }
}()
time.Sleep(time.Second)
kill <- struct{}{}

这可能不是您正在寻找的内容,因为Read即使您发送到kill. 但是,处理传入读取的 goroutine 将终止。


推荐阅读