我用Golang写了一个端口转发工具,但是性能极其低下,求各位帮忙分析分析

源码在这里:

https://github.com/cw1997/net...

package main

import (
    //"fmt"
    "net"
    //"os"
    //"bufio"
    "log"
    "io/ioutil"
)

const (
    ip   = ""
    port = 8899
)

func main() {
    listen, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP(ip), port, ""})
    if err != nil {
        log.Println("监听端口失败:", err.Error())
        return
    }
    log.Println("已初始化连接,等待客户端连接...")
    Server(listen)
}

func Server(listen *net.TCPListener) {
    for {
        conn, err := listen.AcceptTCP()
        if err != nil {
            log.Println("接受客户端连接异常:", err.Error())
            continue
        }
        log.Println("客户端连接来自:", conn.RemoteAddr().String())
        defer conn.Close()
        go func() {
            data := make([]byte, 8192)
            for {
                i, err := conn.Read(data)
                //log.Println("客户端发来数据:\n", string(data[0:i]))
                // log.Println("客户端发来数据:\n", data)
                log.Println("客户端发来数据:", i)
                if err != nil {
                    log.Println("读取客户端数据错误:", err.Error())
                    break
                }
                go conn.Write(Send(data))
            }
        }()
    }
}

func Send(data []byte) (buf []byte) {
    pTCPConn, err := net.Dial("tcp", "127.0.0.1:80")
    if err != nil {
        //log.Fprintf(os.Stderr, "Error: %s", err.Error())
        log.Printf("Error: %s", err.Error())
        return
    }

    n, errWrite := pTCPConn.Write(data)
    if errWrite != nil {
        //log.Fprintf(os.Stderr, "Error: %s", errWrite.Error())
        log.Printf("Error: %s", errWrite.Error())
        return
    }
    defer pTCPConn.Close()

    //log.Fprintf(os.Stdout, "writed: %d\n", n)
    log.Printf("writed: %d\n", n)

    buf, errRead := ioutil.ReadAll(pTCPConn)
    //log.Println("服务端发来数据:\n", string(buf))
    log.Println("服务端发来数据:", len(buf))
    if errRead != nil {
        //log.Fprintf(os.Stderr, "Error: %s", errRead.Error())
        log.Printf("Error: %s", errRead.Error())
        return
    }
    //r := len(buf)
    //fmt.Fprintf(os.Stdout, string(buf[:r]))
    //fmt.Fprintf(os.Stdout, "readed: %v\n", buf)
    return
}

我用这个小工具代理一个开在内网的HTTP服务器,但是访问延时很大,一个很简单的phpinfo页面都要5s才能打开,其他页面资源比较多的网页那就更不用说了,卡的要命,求各位帮忙分析分析问题出在哪啊?(还有我是新手,很多内置库可能用法有误,还求各位轻喷并且帮忙指出来一下,谢谢各位老鸟帮忙)

另外我这个代码里面是写死了read buffer缓冲区为8192字节,如果遇到一个HTTP包尺寸很大该怎么处理呢?把这个[]byte的长度无限make大也不是办法啊

阅读 8.1k
2 个回答

mdzz

我就问一哈,

ReadAll是个啥?你为啥会想到这么用?

流和块的处理机制不一样啊。
转发就是流转发啊,readall是用在块上面的啊。

举个例子,readall基本你能看到的地方时,http请求结束之后,进行一次readall。他并不适合处理流量。

我建议用标准的方法。

这是一个转发到docker的proxy示例代码:
https://github.com/UESTC-BBS/...

核心代码是:

func forward(uconn net.Conn) {
    tconn, err := net.Dial("tcp", ADDRESS)

    var wg sync.WaitGroup
    go func(uconn net.Conn, tconn net.Conn) {
        wg.Add(1)
        defer wg.Done()
        io.Copy(uconn, tconn)
        uconn.Close()
    }(uconn, tconn)
    go func(uconn net.Conn, tconn net.Conn) {
        wg.Add(1)
        defer wg.Done()
        io.Copy(tconn, uconn)
        tconn.Close()
    }(uconn, tconn)
    wg.Wait()
}

还有点话要说,等下班了再写


叨逼几句:

Go语言中,

流量转发,一定是对readwriter1进行读取,写入到readwriter2,同时对readwriter2读取,写入到readwriter1,即:io.Copy。

流量拷贝,一定是使用multiwriter。

read,readall等操作,一般用于需要对流进行拆包的地方进行块处理,例如各种tcp(im、游戏)框架。

当然有特例,例如:单端口兼容多服务,https://github.com/jamescun/s...

他肯定必须用了io.Copy,但是他肯定还用了块操作,不然没办法判断协议。

clipboard.png

看了一下,Send函数在每次调用的时候都发生了dial,这可能是导致性能底下的原因之一.
大致写了下思路,没有错误处理和完善.

conn, err := linstener.Accept()
go func(conn net.Conn){
    rConn, err := net.Dial("tcp", "ip:port")
    go func(){io.Copy(conn, rConn)}()
    io.Copy(rConn, conn)
}(conn)
推荐问题
宣传栏