我用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大也不是办法啊

阅读 2.7k
评论 2017-06-07 提问
    2 个回答
    大舒
    • 6.9k

    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

    评论 赞赏