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日

悬赏问题

  • ¥20 求快手直播间榜单匿名采集ID用户名简单能学会的
  • ¥15 DS18B20内部ADC模数转换器
  • ¥15 做个有关计算的小程序
  • ¥15 MPI读取tif文件无法正常给各进程分配路径
  • ¥15 如何用MATLAB实现以下三个公式(有相互嵌套)
  • ¥30 关于#算法#的问题:运用EViews第九版本进行一系列计量经济学的时间数列数据回归分析预测问题 求各位帮我解答一下
  • ¥15 setInterval 页面闪烁,怎么解决
  • ¥15 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题
  • ¥15 如何用Python爬取各高校教师公开的教育和工作经历