当前位置: 首页 > 科技观察

Golang从TCP升级到WebSocket

时间:2023-03-22 11:39:10 科技观察

在今天发表的文章中,作者记录了从TCP成功升级到WebSocket的操作,时间紧任务重。有一台服务器,本来是TCP的私有协议,突然需要支持WebSocket,想在原来的端口上强制使用WebSocket。最后还是比较简单的成功了。不得不说golang很舒服。WebSocket库从TCP升级到WebSocket。如何使用WebSocket库。/src/golang.org/x/net)。官方的websocket库使用起来非常简单。接口文档请参考:websocket·pkg.go.dev。Clientconn,err:=websocket.Dial(url,subprotocol,origin)websocket.Message.Send(conn,"")//发送一个stringvarb[]bytewebsocket.Message.Receive(conn,&b)//接收一个[]字节客户端使用websocket.Dial()创建连接*websocket.Conn。其中:subprotocol表示细分的协议格式(比如各种不同的序列化方式),默认可以为空Conn有Read/Write等方法,但是使用websocket.Message更方便,因为可以保证一个数据包的完整性。服务器varrecvfunc([]byte)varerrerrorf:=func(conn*websocket.Conn){for{varb[]byteerr=websocket.Message.Receive(conn,&b)iferr!=nil{return}else{recv(b)}}}websocket.Handler(f).ServeHTTP(w,r)使用websocket.Handler或websocket.Server进行升级(Upgrade)HTTP请求,在回调中接收一个*websocket.Conn供业务方使用。Handler和Server都可以在net.http中注册,也可以自己调用ServeHTTP方法。Handler只有一个简单的回调函数接口。使用闭包,您可以使用更多的上下文来连接和发送。如上一篇文章所示,您可以使用websocket.Message进行简单的二进制或字符串发送和接收,一次完成一个数据包。websocket.Codec也可以支持序列化和反序列化,直接收发golang对象。你只需要定义两个函数,一个用于序列化,一个用于反序列化。websocket.JSON是一个预建的解码器。另外websocket.Message也是一个解码器。当然*websocket.Conn本身也实现了net.Conn,它有RemoteAddr、Read、Write等方法。仅仅使用Read/Write会掩盖WebSocket协议的封装,这是不必要的。从TCP升级到WebSocket幸运的是,TCP私有协议与WebSocket握手协议具有完全不同的协议头。所以判断前三个字节是不是GET,就可以区分是否中转WebSocket。服务端可以通过Handler.ServeHTTP()创建*websocket.Conn,但是对于TCP协议来说,只有一个*net.TCPConn,部分内容已经阅读。现在我们需要把一个[]byte+*net.TCPConn变成http.ResponseWriter+*http.Request。http.ResponseWriterhttp.ResponseWriter是一个很容易模拟的接口,WebSocket会通过Hijack中转,所以可以暴力实现:wsFakeWriter{w:=new(wsFakeWriter)w.conn=connw.rw=bufio.NewReadWriter(bufio.NewReader(conn),bufio.NewWriter(conn))returnw}func(w*wsFakeWriter)Header()http.Header{returnnil}func(w*wsFakeWriter)WriteHeader(int){//处理升级失败??}func(w*wsFakeWriter)Write(b[]byte)(int,error){return0,nil}func(w*wsFakeWriter)Hijack()(net.Conn,*bufio.ReadWriter,error){returnw.conn,w.rw,nil}*http.Request对于*http.Request,幸好有http.ReadRequest()可用:funcReadRequest(b*bufio.Reader)(*Request,error)只需要把[]byte和*net.TCPConn被打包成bufio.Reader:)(*http.Request,error){r:=joinBufferAndReader(buffer,conn)returnhttp.ReadRequest(bufio.NewReader(r))}得益于golang强大的接口自动认证,这个打包过程甚至不需要做太多,只需要调用标准库就可以了[]byte可以变成io.Reader,*net.TCPConn自然就是io.Reader,标准库也可以拼接io.Reader,一切都很完美。使用方法funcWebsocketOnTCP(buffer[]byte,conn*net.TCPConn,recvfunc(*Package))error{req,err:=takeHttpRequest(buffer,conn)iferr!=nil{returnerr}f:=func(ws*websocket.Conn){err=doWebSocket(ws,recv)}w:=makeHttpResponseWriter(conn)websocket.Handler(f).ServeHTTP(w,req)returnerr}funcdoWebSocket(conn*websocket.Conn,recvfunc(*Package))错误{remoteAddr:=conn.RemoteAddr()reply:=func(b[]byte)error{websocket.Message.Send(conn,b)//这里线程不安全}for{varb[]byteerr:=websocket.Message.Receive(conn,&b)iferr!=nil{returnerr}pack:=new(Package)pack.Addr=remoteAddrpack.Content=bpack.Reply=replyrecv(pack)}}以上只是粗略的使用方法,简单发送和接收即可成功。只是没有验证线程安全,升级失败等。

最新推荐
猜你喜欢