【问题标题】:io.Copy in goroutine to prevent blockingio.Copy 在 goroutine 中防止阻塞
【发布时间】:2020-10-12 18:40:15
【问题描述】:

我正在阅读《黑帽围棋》这本书。我遇到了一个简单的 TCP 代理示例。它基本上只是转发请求并发回回复。代理代码的要点如下(修改):

func handle(src net.Conn) {
    dst, err := net.Dial("tcp", "example.com:80")
    if err != nil {
        log.Fatalln("Unable to connect to remote")
    }
    defer dst.Close()

    // Run in goroutine to prevent io.Copy from blocking
    go func() {
        if _, err := io.Copy(dst, src); err != nil {
            log.Fatalln("Something wrong src -> dst")
        }
    }()

    if _, err := io.Copy(src, dst); err != nil {
        log.Fatalln("Something wrong dst -> src")
    }
}

我不明白的部分是注释“在 goroutine 中运行以防止 io.Copy 阻塞”。我已经在有和没有将它包装在 goroutine 中的情况下运行它,它只适用于 goroutine,但我不明白为什么。我们不能为请求运行一个阻塞io.Copy,然后为响应运行另一个吗?如果第一个 io.Copy 在 goroutine 中运行,我想我很难理解我们如何保证订购。

【问题讨论】:

  • TCP是双工流;没有“请求”和“响应”,所以你不能复制一个然后另一个。
  • 我了解 TCP,但我想你找到了我问题的核心。我正在考虑使用封装的 HTTP 数据包,而不是转发整个流。感谢您的帮助。

标签: go


【解决方案1】:

当您处理 TCP 流时,您必须处理双向的流量,从 src 到 dest 和从 dest 到 src。您可以通过两次复制操作来做到这一点。但是,复制将阻塞,直到它收到错误或 EOF(即套接字关闭),因此您必须将这些复制操作中的至少一个放到 goroutine 中。它的写入方式,一个复制操作将从一个套接字读取并写入另一个,另一个复制操作将执行相反的操作,直到出现错误或 EOF。当收到错误或 EOF 时,两个复制操作都将停止。

【讨论】:

    【解决方案2】:

    读取器或写入器返回错误(包括io.EOF)后,io.Copy 函数返回。如果 io.Copy 调用未在 Go 例程中运行,则在将任何数据从 dst 复制到 src 之前,所有数据都会从 src 复制到 dst。

    【讨论】:

      猜你喜欢
      • 2020-04-03
      • 1970-01-01
      • 2022-09-26
      • 1970-01-01
      • 2021-11-01
      • 1970-01-01
      • 2015-04-22
      • 2016-10-26
      • 1970-01-01
      相关资源
      最近更新 更多