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

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

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

问题

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

模式一:

package main

import (
	"io"
	"net"
	"sync"
)

func proxy1(conn1, conn2 net.Conn) {
	defer conn1.Close()
	defer conn2.Close()

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		io.Copy(conn1, conn2)
		wg.Done()
	}()
	go func() {
		io.Copy(conn2, conn1)
		wg.Done()
	}()

	wg.Wait()
}

模式二:

package main

import (
	"io"
	"net"
	"sync"
)

func proxy2(conn1, conn2 net.Conn) {
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		io.Copy(conn1, conn2)
		wg.Done()
		conn1.Close()
	}()
	go func() {
		io.Copy(conn2, conn1)
		wg.Done()
		conn2.Close()
	}()

	wg.Wait()
}

这两种模式都可以在测试中正常工作,但是你可能会担心边缘情况。其中,模式一在两个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?

package main

import (
	"io"
	"net"
	"sync"
)

func proxy1(conn1, conn2 net.Conn) {
	defer conn1.Close()
	defer conn2.Close()

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		io.Copy(conn1, conn2)
		wg.Done()
	}()
	go func() {
		io.Copy(conn2, conn1)
		wg.Done()
	}()

	wg.Wait()
}

func proxy2(conn1, conn2 net.Conn) {
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		io.Copy(conn1, conn2)
		wg.Done()
		conn1.Close()
	}()
	go func() {
		io.Copy(conn2, conn1)
		wg.Done()
		conn2.Close()
	}()

	wg.Wait()
}

答案1

得分: 2

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

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

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

func proxy(conn1, conn2 *net.TCPConn) {
    defer conn1.Close()
    defer conn2.Close()

    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        defer wg.Done()
        io.Copy(conn1, conn2)
        // 向对等方发送信号,表示没有更多的数据了。
        conn1.CloseWrite()
    }()
    go func() {
        defer wg.Done()
        io.Copy(conn2, conn1)
        // 向对等方发送信号,表示没有更多的数据了。
        conn2.CloseWrite()
    }()

    wg.Wait()
}
英文:

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:

func proxy(conn1, conn2 *net.TCPConn) {
	defer conn1.Close()
	defer conn2.Close()

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()
		io.Copy(conn1, conn2)
        // Signal peer that no more data is coming.
		conn1.CloseWrite()
	}()
	go func() {
		defer wg.Done()
		io.Copy(conn2, conn1)
        // Signal peer that no more data is coming.
		conn2.CloseWrite()
	}()

	wg.Wait()
}

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:

确定