douyun1852 2015-08-21 08:30
浏览 249
已采纳

是否可以通过websockets或其他协议传输TCP连接?

I'm wondering, is it possible to transport/tunnel a TCP connection though the websockets protocol?

There is a websockets package and it seems I could io.copy the TCP connection into the websockets connection but I don't know how to reassemble it as TCPConn on the other end.

For example I would like to create a socks5 ServerN(behind NAT) on one end and a ServerIE on the other end. An internet user would make a socks5 request (though the socks5 protocol) to ServerIE and ServerIE would send the socks5 request through the websocket connection to ServerNAT. ServerNAT will then process the socks5 request(i.e. it's a socks5 server).

EDIT: I've wrote some code but unfortunately it gets stuck on reading from the TCP connection into WS. To run it: go run main.go -logtostderr

package main

import (
    "flag"

    log "github.com/golang/glog"
    "golang.org/x/net/websocket"
    "bufio"
    "net"
    "net/http"
    "time"
//  "io"
)

func main() {
    flag.Parse()
    defer log.Flush()
    log.Infof("wsServer()")
    wsCh := wsServer()
    log.Infof("wsNAT()")
    natWS, err := wsNAT()
    if err != nil {
        log.Error(err)
        return
    }
    done := make(chan struct{}, 1)
        log.Infof("listenTCP()")
        conn := listenTCP()
        buf := bufio.NewReader(conn)
    go func() {
        log.Infof("Write TCP to WS")
        n, err := buf.WriteTo(natWS)
        if err != nil {
            log.Fatal(err)
            return
        }
        log.Infof("newConn.ws.ReadFrom(conn) %v", n)
        close(done)
    }()
    wsToTCP(<- wsCh)
    <-done
}
// wsNAT dials /ws endpoint and returns a websocket connection.
func wsNAT() (*websocket.Conn, error) {
    time.Sleep(1 * time.Second)
    origin := "http://localhost/"
    url := "ws://localhost:12345/ws"
    return websocket.Dial(url, "", origin)
}
 // wsServer returns the websocket
 // connections received on /ws endpoint
func wsServer() chan *websocket.Conn {
    wsCh := make(chan *websocket.Conn, 1)
    http.Handle("/ws", websocket.Handler(func(ws *websocket.Conn) {
        wsCh <- ws
        time.Sleep(1 *time.Hour)
    }))
    go func() {
        err := http.ListenAndServe(":12345", nil)
        if err != nil {
            panic("ListenAndServe: " + err.Error())
        }
    }()
    return wsCh
}
// wsToTCP reads a ws connection and converts its payload
// to a TCP connection.
func wsToTCP(ws *websocket.Conn) {
    conn := &net.TCPConn{}
    var msg []byte
    if _, err := ws.Read(msg); err != nil {
        log.Error(err)
        return
    }
    log.Infof("msg %v", msg)
    n, err := conn.Write(msg)
    if err != nil {
        log.Errorf("conn.Write %v, msg %v", err, msg)
        return
    }
    log.Infof("conn is %v, copied %v", conn, n)
    // END: do something with the connection
    // ..... 
}
//ListenTCP returns the first TCP connection received on port 8080
func listenTCP() *net.TCPConn {
    addr := &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 8080}
    lr, err := net.ListenTCP("tcp", addr)
    if err != nil {
        panic(err)

    }
    defer lr.Close()
    ieConn, err := lr.AcceptTCP()
    if err != nil {
        panic(err)
    }
    log.Infof("connection accepted")
    return ieConn
}

To trigger the TCP listener you can issue a request on port 8080 (e.g. curl --socks5 localhost:8080 google.com -v or curl localhost:8080 -v)

  • 写回答

1条回答 默认 最新

  • douciping4283 2015-08-21 19:44
    关注

    This shouldn't be any problem. I've built TCP forwarders over a number of protocols. Never WebSockets, but I don't see a reason it would be different.

    You have two problems:

    • The correct way to make a connection is with net.Dial, not by just creating a net.TCPConn.
    • You're only copying in one direction. You need to copy in both directions.

    Here's a super simple proxy that I built this morning for a different purpose (my full code corrupts a packet every so often so I can test bad connectoins). It should be convertible to many other purposes:

    func run(inPort int, dest string) {
        l, err := net.Listen("tcp", fmt.Sprintf(":%d", inPort))
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
        fmt.Printf("http://%v -> %s
    ", l.Addr(), dest)
    
        for {
            conn, err := l.Accept()
            if err != nil {
                log.Fatal(err)
            }
    
            fmt.Printf("Connection from %s
    ", conn.RemoteAddr())
            go proxy(conn, dest)
        }
    }
    
    func proxy(in net.Conn, dest string) {
        out, err := net.Dial("tcp", dest)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    
        go io.Copy(out, in)
        io.Copy(in, out)
        fmt.Printf("Disconnect from %s
    ", in.RemoteAddr())
    }
    

    You just need to replace the net.Dial here with a WS call to your server, and on the server side use net.Dial to forward it along.

    But probably the most key point is the last few lines. You have to copy in both directions and people forget that.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 请选择合适的标签 8月1日

悬赏问题

  • ¥15 使用C#,asp.net读取Excel文件并保存到Oracle数据库
  • ¥15 C# datagridview 单元格显示进度及值
  • ¥15 thinkphp6配合social login单点登录问题
  • ¥15 HFSS 中的 H 场图与 MATLAB 中绘制的 B1 场 部分对应不上
  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 虚心请教几个问题,小生先有礼了
  • ¥30 截图中的mathematics程序转换成matlab