go语言实现TCP端口转发

作者: CodingCode | 来源:发表于2017-10-12 15:45 被阅读130次

    有些时候项目会碰到需要端口转发的需求,比如:

    1. 一个服务发布在公共端口上,客户端进来的请求需要转发到另一个本地端口,或者另一台主机;或者
    2. 一个公共服务需要转到到多台内部服务上,轮询,按优先级等;或者
    3. 一个服务请求进来之前需要做请求检查,例如安全功能参数等检查,类似网关功能,只有验证通过的请求才能发送给实际服务器。
    4. 等等等

    下面是一个实现TCP端口转发功能的代码例子。
    这里例子将会起一个监听在端口fromport,然后把所有向此fromport发起的请求都转发到端口toport,当然必须先有另一个服务已经监听在toport接受请求服务。

    package main
    
    import (
        "os"
        "fmt"
        "net"
    )
    
    // Start a proxy server listen on fromport
    // this proxy will then forward all request from fromport to toport
    //
    // Notice: a service must has been started on toport
    func proxyStart(fromport, toport int) {
        proxyaddr := fmt.Sprintf(":%d", fromport)
        proxylistener, err := net.Listen("tcp", proxyaddr)
        if err != nil {
            fmt.Println("Unable to listen on: %s, error: %s\n", proxyaddr, err.Error())
            os.Exit(1)
        }
        defer proxylistener.Close()
    
        for {
            proxyconn, err := proxylistener.Accept()
            if err != nil {
                fmt.Printf("Unable to accept a request, error: %s\n", err.Error())
                continue
            }
    
            // Read a header firstly in case you could have opportunity to check request
            // whether to decline or proceed the request
            buffer := make([]byte, 1024)
            n, err := proxyconn.Read(buffer)
            if err != nil {
                fmt.Printf("Unable to read from input, error: %s\n", err.Error())
                continue
            }
    
            // TODO
            // Your choice to make decision based on request header
    
            targetaddr := fmt.Sprintf("localhost:%d", toport);
            targetconn, err := net.Dial("tcp", targetaddr)
            if err != nil {
                fmt.Println("Unable to connect to: %s, error: %s\n", targetaddr, err.Error())
                proxyconn.Close()
                continue
            }
    
            n, err = targetconn.Write(buffer[:n])
            if err != nil {
                fmt.Printf("Unable to write to output, error: %s\n", err.Error())
                proxyconn.Close()
                targetconn.Close()
                continue 
            }
    
            go proxyRequest(proxyconn, targetconn)
            go proxyRequest(targetconn, proxyconn)
        }
    }
    
    // Forward all requests from r to w
    func proxyRequest(r net.Conn, w net.Conn) {
        defer r.Close()
        defer w.Close()
    
        var buffer = make([]byte, 4096000)
        for {
            n, err := r.Read(buffer)
            if err != nil {
                fmt.Printf("Unable to read from input, error: %s\n", err.Error())
                break
            }
    
            n, err = w.Write(buffer[:n])
            if err != nil {
                fmt.Printf("Unable to write to output, error: %s\n", err.Error())
                break
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:go语言实现TCP端口转发

        本文链接:https://www.haomeiwen.com/subject/yaxoyxtx.html