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

回答

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,但是他肯定还用了块操作,不然没办法判断协议。

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

看了一下,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)

以上是 【go】我用Golang写了一个端口转发工具,但是性能极其低下,求各位帮忙分析分析 的全部内容, 来源链接: utcz.com/a/106776.html

回到顶部