【问题标题】:How can i interrupt a goroutine executing (*TCPListener) Accept?如何中断执行 (*TCPListener) Accept 的 goroutine?
【发布时间】:2013-09-16 09:59:19
【问题描述】:

我最近在玩 go 并尝试创建一些服务器来响应 tcp 连接上的客户端。

我的问题是我如何干净地关闭服务器并中断当前在以下调用中“阻塞”的 go-routine

func (*TCPListener) 接受?

根据documentation of Accept

Accept 在 Listener 接口中实现 Accept 方法;它等待下一次调用并返回一个通用的 Conn。

这些错误也很少被记录。

【问题讨论】:

    标签: go


    【解决方案1】:

    这就是我想要的。也许将来会帮助某人。 注意使用 select 和 "c" 通道将它与退出通道结合起来

        ln, err := net.Listen("tcp", ":8080")
        if err != nil {
            // handle error
        }
        defer ln.Close()
        for {
            type accepted struct {
                conn net.Conn
                err  error
            }
            c := make(chan accepted, 1)
            go func() {
                conn, err := ln.Accept()
                c <- accepted{conn, err}
            }()
            select {
            case a := <-c:
                if a.err != nil {
                    // handle error
                    continue
                }
                go handleConnection(a.conn)
            case e := <-ev:
                // handle event
                return
            }
        }
    

    【讨论】:

    • 我相信这会泄漏 goroutine,因为没有什么可以保证通道 c 会被耗尽。如果由于循环终止而变满,则剩余的发送将永远不会完成。
    【解决方案2】:

    TCPListener 截止日期

    您不一定需要额外的 go 例程(不断接受),只需指定 Deadline

    for 示例:

    for {
    
        // Check if someone wants to interrupt accepting
        select {
        case <- someoneWantsToEndMe: 
            return // runs into "defer listener.Close()"
        default: // nothing to do
        }
    
        // Accept with Deadline
        listener.SetDeadline(time.Now().Add(1 * time.Second)
        conn, err := listener.Accept()
        if err != nil {
            // TODO: Could do some err checking (to be sure it is a timeout), but for brevity
            continue
        }
    
        go handleConnection(conn)
    }
    

    【讨论】:

    • net.Listener 接口没有定义 SetDeadline 方法,您将其与 net.Conn 接口混为一谈。要停止 listener.Accept() 等待,您只需从另一个 goroutine 调用 listener.Close()。
    • @ftl:也是一种可能,但也可能如上所示(顺便说一句(恕我直言)非常优雅/简单/清晰)。 OP 询问了 TCPListener,参考文档:golang.org/pkg/net/#TCPListener.SetDeadline
    • 恕我直言,这是更好的方法。
    【解决方案3】:

    只需 Close() 和从 net.Listen(...) 调用中获得的 net.Listener 并从正在执行的 goroutine 返回。

    【讨论】:

    • 好的,但是我如何在不创建一些应用程序特定代码的情况下识别由于我的关闭而接受失败?根据golang.org/pkg/net/#Error 的文档,这不容易掌握。好吧,也许应该无关紧要,但如果我知道套接字已关闭,那将是更加封装的流程。
    • Accept 返回两个值:Conn 和错误。如果错误不为零,则说明失败。
    • 我知道。但是你怎么知道 Accept 没有因为其他原因失败而我只需要重试?文档并没有说只有当我关闭套接字时它才会失败。另请查看 posix accept 的文档。 linux.die.net/man/2/accept。看看那里定义了多少错误。
    • 检查错误的类型为例如net.OpErrornet 包中定义的其他错误类型。 _, ok := err.(net.OpError) 将使 ok 成为 truefalse 取决于 err 实际上是否属于 net.OpError 类型
    猜你喜欢
    • 1970-01-01
    • 2011-02-28
    • 2016-02-13
    • 1970-01-01
    • 2018-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多