实现TCP连接的双向拷贝

io.Copy来实现内容拷贝,将源端数据复制到目标端,为防止内存溢出,

io.Copy方法默认缓冲区为32k,io.CopyBuffer方法可以设置缓冲区大小

用一个goroutine从server拷贝到client,再用另外一个goroutine从client拷贝到server。任何一方断开连接,双向都断开连接。

实现方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main

import (
    "io"
    "log"
    "net"
)

func main() {
    listener, err := net.Listen("tcp", "127.0.0.1:8087")
    if err != nil {
        panic(err)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            panic(err)
        }
        go handle(conn.(*net.TCPConn))
    }
}

func handle(server *net.TCPConn) {
    client, err := net.Dial("tcp", "127.0.0.1:8088")
    if err != nil {
        log.Printf("dial err: %v\n", err)
        return
    }

    go func() {
        buf := make([]byte, 2048)
        _, err := io.CopyBuffer(server, client, buf)
        if err != nil {
            log.Printf("server err %v\n", err)
        }
        server.Close()
    }()

    buf := make([]byte, 2048)
    _, err = io.CopyBuffer(client, server, buf)
    if err != nil {
        log.Printf("client err %v\n", err)
    }
    client.Close()
}

运行测试

  • 启动一个tcp服务,监听8088端口
1
2
$ nc -lk 127.0.0.1 8088
hello
  • 运行tcp拷贝服务,监听8087端口,转发到8088端口
1
2
$ GO111MODULE=off go run main.go

  • 创建一个tcp连接到8087端口,并发送hello消息,8088端口收到消息
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ nc -v 127.0.0.1 8087
found 0 associations
found 1 connections:
     1: flags=82<CONNECTED,PREFERRED>
        outif lo0
        src 127.0.0.1 port 58804
        dst 127.0.0.1 port 8088
        rank info not available
        TCP aux info available

Connection to 127.0.0.1 port 8088 [tcp/radan-http] succeeded!
hello

参考