在Golang中正确使用io.Copy在两个net.Conn TCP连接之间代理数据的方法是什么?

huangapple go评论114阅读模式
英文:

Correct usage of io.Copy to proxy data between two net.Conn TCP connections in Golang?

问题

在Golang中,复制两个TCP连接之间的数据有不同的用法。以下是两种常见的模式:

模式一:

  1. package main
  2. import (
  3. "io"
  4. "net"
  5. "sync"
  6. )
  7. func proxy1(conn1, conn2 net.Conn) {
  8. defer conn1.Close()
  9. defer conn2.Close()
  10. var wg sync.WaitGroup
  11. wg.Add(2)
  12. go func() {
  13. io.Copy(conn1, conn2)
  14. wg.Done()
  15. }()
  16. go func() {
  17. io.Copy(conn2, conn1)
  18. wg.Done()
  19. }()
  20. wg.Wait()
  21. }

模式二:

  1. package main
  2. import (
  3. "io"
  4. "net"
  5. "sync"
  6. )
  7. func proxy2(conn1, conn2 net.Conn) {
  8. var wg sync.WaitGroup
  9. wg.Add(2)
  10. go func() {
  11. io.Copy(conn1, conn2)
  12. wg.Done()
  13. conn1.Close()
  14. }()
  15. go func() {
  16. io.Copy(conn2, conn1)
  17. wg.Done()
  18. conn2.Close()
  19. }()
  20. wg.Wait()
  21. }

这两种模式都可以在测试中正常工作,但是你可能会担心边缘情况。其中,模式一在两个goroutine结束后再关闭连接,而模式二在关闭连接之前就结束了goroutine。具体使用哪种模式取决于你的需求和场景。

英文:

There are conflicting usages of io.Copy and I want to know the correct way to copy data between two TCP connections in Golang?

Some people say to call Close inside the goroutines, and others don't
https://stackoverflow.com/questions/32460618/golang-1-5-io-copy-blocked-with-two-tcpconn
https://github.com/LiamHaworth/go-tproxy/blob/master/example/tproxy_example.go#L159

Here are the two common patterns I've seen online, both seemly work fine in my tests, but I'm worried about edge cases and I cannot figure out which, if any, is correct?

  1. package main
  2. import (
  3. "io"
  4. "net"
  5. "sync"
  6. )
  7. func proxy1(conn1, conn2 net.Conn) {
  8. defer conn1.Close()
  9. defer conn2.Close()
  10. var wg sync.WaitGroup
  11. wg.Add(2)
  12. go func() {
  13. io.Copy(conn1, conn2)
  14. wg.Done()
  15. }()
  16. go func() {
  17. io.Copy(conn2, conn1)
  18. wg.Done()
  19. }()
  20. wg.Wait()
  21. }
  22. func proxy2(conn1, conn2 net.Conn) {
  23. var wg sync.WaitGroup
  24. wg.Add(2)
  25. go func() {
  26. io.Copy(conn1, conn2)
  27. wg.Done()
  28. conn1.Close()
  29. }()
  30. go func() {
  31. io.Copy(conn2, conn1)
  32. wg.Done()
  33. conn2.Close()
  34. }()
  35. wg.Wait()
  36. }

答案1

得分: 2

代理1:当一个对等方在对等方关闭连接之前等待EOF时,代理会发生死锁。

代理2:代理可以在从连接中读取所有数据之前关闭连接。

以下代码修复了这两个问题:

  1. func proxy(conn1, conn2 *net.TCPConn) {
  2. defer conn1.Close()
  3. defer conn2.Close()
  4. var wg sync.WaitGroup
  5. wg.Add(2)
  6. go func() {
  7. defer wg.Done()
  8. io.Copy(conn1, conn2)
  9. // 向对等方发送信号,表示没有更多的数据了。
  10. conn1.CloseWrite()
  11. }()
  12. go func() {
  13. defer wg.Done()
  14. io.Copy(conn2, conn1)
  15. // 向对等方发送信号,表示没有更多的数据了。
  16. conn2.CloseWrite()
  17. }()
  18. wg.Wait()
  19. }
英文:

Proxy 1: The proxy deadlocks when a peer waits for EOF before the peer closes the connection.

Proxy 2: The proxy can close a connection before all data is read from the connection.

This code fixes both problems:

  1. func proxy(conn1, conn2 *net.TCPConn) {
  2. defer conn1.Close()
  3. defer conn2.Close()
  4. var wg sync.WaitGroup
  5. wg.Add(2)
  6. go func() {
  7. defer wg.Done()
  8. io.Copy(conn1, conn2)
  9. // Signal peer that no more data is coming.
  10. conn1.CloseWrite()
  11. }()
  12. go func() {
  13. defer wg.Done()
  14. io.Copy(conn2, conn1)
  15. // Signal peer that no more data is coming.
  16. conn2.CloseWrite()
  17. }()
  18. wg.Wait()
  19. }

huangapple
  • 本文由 发表于 2023年2月11日 13:05:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/75418196.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定