Golang带流量限制Socks5代理转发后下载速度归零问题
带流量限制的Socks5代理转发后下载速度归零问题排查
我实现了一个带流量限制的Golang Socks5代理(监听10801端口),将流量转发至另一个无流量限制的Socks5代理(监听10802端口)。使用命令curl -x socks5://127.0.0.1:10801 -O https://mirrors.xxxx.iso测试下载时,初期下载正常且流量限制生效,但一段时间后速度变为0。
我尝试了两种流量限制的Copy实现方式,即使将缓冲区增大至64*1024,问题仍存在。
带流量限制的代理代码
package main import ( "fmt" "github.com/juju/ratelimit" "io" "net" ) var nextProxy = "127.0.0.1:10802" func main() { server, err := net.Listen("tcp", ":10801") if err != nil { fmt.Printf("Listen failed: %v\n", err) return } for { client, err := server.Accept() if err != nil { fmt.Printf("Accept failed: %v", err) continue } go handle(client) } } func handle(conn net.Conn) { // add close defer conn.Close() // next socks5 proxy nextConn, err := net.Dial("tcp", nextProxy) if err != nil { fmt.Println("Failed to connect to next proxy:", err) conn.Close() return } defer nextConn.Close() go func() { _, err := Copy(nextConn, conn) if err != nil { fmt.Println("Error copying data to next proxy:", err) conn.Close() } }() _, err = Copy(conn, nextConn) if err != nil { fmt.Println("Error copying data from next proxy:", err) conn.Close() } } func Copy(left io.Writer, right io.Reader) (int64, error) { right = ratelimit.Reader(right, ratelimit.NewBucketWithRate(102400, 102400)) return io.Copy(left, right) }
另一种流量限制Copy实现
我还尝试了另一种Copy实现:
func copyWithRateLimit(dst io.Writer, src io.Reader, limiter *ratelimit.Bucket) { // buffer buf := make([]byte, 32*1024) // 32KB buffer for { n, err := src.Read(buf) if err != nil { if err == io.EOF { return } fmt.Println("Error reading from source:", err) return } // Rate limit the data if limit := limiter.WaitMaxDuration(int64(n), time.Second); !limit { fmt.Println("Error rate limiting:", err) return } if _, err := dst.Write(buf[:n]); err != nil { fmt.Println("Error writing to destination:", err) return } } }
即使将缓冲区增大至64*1024,速度仍会归零并最终无法正常工作。
无流量限制的Socks5代理代码
package main import ( "encoding/binary" "errors" "fmt" "io" "net" ) func main() { server, err := net.Listen("tcp", ":10802") if err != nil { fmt.Printf("Listen failed: %v\n", err) return } for { client, err := server.Accept() if err != nil { fmt.Printf("Accept failed: %v", err) continue } go process(client) } } func process(client net.Conn) { if err := Socks5Auth(client); err != nil { fmt.Println("auth error:", err) client.Close() return } target, err := Socks5Connect(client) if err != nil { fmt.Println("connect error:", err) client.Close() return } Socks5Forward(client, target) } func Socks5Auth(client net.Conn) (err error) { buf := make([]byte, 256) n, err := io.ReadFull(client, buf[:2]) if n != 2 { return errors.New("reading header: " + err.Error()) } ver, nMethods := int(buf[0]), int(buf[1]) if ver != 5 { return errors.New("invalid version") } n, err = io.ReadFull(client, buf[:nMethods]) if n != nMethods { return errors.New("reading methods: " + err.Error()) } n, err = client.Write([]byte{0x05, 0x00}) if n != 2 || err != nil { return errors.New("write rsp: " + err.Error()) } return nil } func Socks5Connect(client net.Conn) (net.Conn, error) { buf := make([]byte, 256) n, err := io.ReadFull(client, buf[:4]) if n != 4 { return nil, errors.New("read header: " + err.Error()) } ver, cmd, _, atyp := buf[0], buf[1], buf[2], buf[3] if ver != 5 || cmd != 1 { return nil, errors.New("invalid ver/cmd") } addr := "" switch atyp { case 1: n, err = io.ReadFull(client, buf[:4]) if n != 4 { return nil, errors.New("invalid IPv4: " + err.Error()) } addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3]) case 3: n, err = io.ReadFull(client, buf[:1]) if n != 1 { return nil, errors.New("invalid hostname: " + err.Error()) } addrLen := int(buf[0]) n, err = io.ReadFull(client, buf[:addrLen]) if n != addrLen { return nil, errors.New("invalid hostname: " + err.Error()) } addr = string(buf[:addrLen]) case 4: return nil, errors.New("IPv6: no supported yet") default: return nil, errors.New("invalid atyp") } n, err = io.ReadFull(client, buf[:2]) if n != 2 { return nil, errors.New("read port: " + err.Error()) } port := binary.BigEndian.Uint16(buf[:2]) destAddrPort := fmt.Sprintf("%s:%d", addr, port) dest, err := net.Dial("tcp", destAddrPort) if err != nil { return nil, errors.New("dial dst: " + err.Error()) } n, err = client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0}) if err != nil { dest.Close() return nil, errors.New("write rsp: " + err.Error()) } return dest, nil } func Socks5Forward(client, target net.Conn) { forward := func(src, dest net.Conn) { defer src.Close() defer dest.Close() io.Copy(src, dest) } go forward(client, target) go forward(target, client) }
测试情况
在同一机器上启动两个程序并使用curl下载文件,出现速度归零的问题。
我进行了以下测试:
- 将第一个代理的带宽设置为与机器一致,curl可正常下载文件;
- 仅通过第二个Socks5代理下载,并在其上设置流量限制,curl正常下载;
- 两个代理设置相同的流量限制,curl可正常下载。
现需排查为何仅第一个代理设置流量限制时,会出现下载速度归零的问题。
内容的提问来源于stack exchange,提问作者Jacky Lam
相关产品推荐
相关产品推荐

