dongyuanliao6204 2018-05-05 09:30
浏览 44

关闭从TCP连接读取的goroutine而不关闭连接

I love the way Go handles I/O multiplexing internally which epoll and another mechanisms and schedules green threads (go-routine here) on its own giving the freedom to write synchronous code.

I know TCP sockets are non-blocking and read will give EAGAIN when no data is available. Given that, conn.Read(buffer) will detect this and blocks the go routine doing a connection read with no data available in the socket buffer. Is there a way to stop such go routine without closing the underlying connection. I am using a connection pool so closing the TCP connection won't make sense for me and want to return that connection back to the pool.

Here is the code to simulate such scenario:

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 {}
}

What are the other approaches can I take if it's not possible:

1) Call syscall.Read and handle this manually. In this case, I need a way to check if the socket is readable before calling syscall.Readotherwise I will end up wasting unnecessary CPU cycles. For my scenario, I think I can skip the event based polling thing and keep on calling syscall.Read as there always be data in my use case.

2) Any suggestions :)

  • 写回答

1条回答 默认 最新

  • dongqiu5184 2018-05-05 18:58
    关注
    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
            }
        }
    }
    

    Send an empty struct to kill from another goroutine to stop receiving from the connection. Here's a program that stops receiving after a second:

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

    This might not be exactly what you're looking for, because the reading goroutine would still be blocked on Read even after you send to kill. However, the goroutine that handles incoming reads would terminate.

    评论

报告相同问题?

悬赏问题

  • ¥15 delta降尺度计算的一些细节,有偿
  • ¥15 Arduino红外遥控代码有问题
  • ¥15 数值计算离散正交多项式
  • ¥30 数值计算均差系数编程
  • ¥15 redis-full-check比较 两个集群的数据出错
  • ¥15 Matlab编程问题
  • ¥15 训练的多模态特征融合模型准确度很低怎么办
  • ¥15 kylin启动报错log4j类冲突
  • ¥15 超声波模块测距控制点灯,灯的闪烁很不稳定,经过调试发现测的距离偏大
  • ¥15 import arcpy出现importing _arcgisscripting 找不到相关程序