最近在学GO语言,需要开发websocket服务器,不想用第三方的websocket服务器框架,准备使用原生TCP硬撸一个websocket服务器。
websocket是基于tcp的协议,对tcp包进行了封装,因此,使用原生tcp服务器,是可以搞定websocket服务器的。
websocket最关键的三个步骤:首次websocket握手、将tcp客户端发来的数据解析成websocket数据、将数据加密成webscoket数据发送给tcp客户端。
websocket 握手:
func (this *SocketServer) Handle(conn net.Conn,buf []byte){ strbuf := string(buf) websocket := dbDriver.RegFind(strbuf,"^GET[\\w\\W]+?Sec-WebSocket-Key: ([\\w\\W]+==)") if len(websocket)==2 { secWebsocketKey := websocket[1] guid := "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" // 计算Sec-WebSocket-Accept h := sha1.New() io.WriteString(h, secWebsocketKey + guid) accept := make([]byte, 28) fmt.Println(string(accept)) base64.StdEncoding.Encode(accept, h.Sum(nil)) response := "HTTP/1.1 101 Switching Protocols\r\n" response = response + "Sec-WebSocket-Accept: " + string(accept) + "\r\n" response = response + "Connection: Upgrade\r\n" response = response + "Upgrade: websocket\r\n\r\n" conn.Write([]byte(response)) }else{ //不是websocket } }
将tcp客户端发来的数据按照websocket格式解析:
func WebSocketParse(data []byte)(string){ en_bytes := []byte("") cn_bytes := make([]int,0) v:= data[1] & 0x7f p:=0 res:="" switch v { case 0x7e: p =4 case 0x7f: p = 10 default: p = 2 } mask := data[p:p+4] data_tmp := data[p+4:] nv :="" nv_bytes :=[]byte("") nv_len :=0 for k,v := range(data_tmp){ nv = string(int(v ^ mask[k%4])) nv_bytes = []byte(nv) nv_len = len(nv_bytes) if nv_len == 1 { en_bytes=BytesCombine(en_bytes,nv_bytes) }else{ en_bytes=BytesCombine(en_bytes,[]byte("%s")) cn_bytes = append(cn_bytes,int(v ^ mask[k%4])) } } //处理中文 cn_str := make([]interface{},0) if len(cn_bytes) >2 { clen := len(cn_bytes) count := int(clen/3) for i:=0;i<count;i++ { mm := i*3 hh := make([]byte,3) h1 ,_ := IntToBytes(cn_bytes[mm],1) h2 ,_ := IntToBytes(cn_bytes[mm+1],1) h3 ,_ := IntToBytes(cn_bytes[mm+2],1) hh[0]=h1[0] hh[1]=h2[0] hh[2]=h3[0] cn_str = append(cn_str, string(hh)) } new := string(bytes.Replace(en_bytes,[]byte("%s%s%s"),[]byte("%s"),-1)) res = fmt.Sprintf(new,cn_str...) }else{ res = string(en_bytes) } return res }
将要发送的数据加密成websocket格式
func BytesCombine(pBytes ...[]byte) []byte { return bytes.Join(pBytes, []byte("")) }func WebSocketSend(data []byte)([]byte){ lenth := len(data) token := string(0x81) if lenth<126 { token+= string(lenth) } bb,_ := IntToBytes(0x81,1) b0 := bb[0] b1 := byte(0) framePos :=0 fmt.Println("长度",lenth) switch { case lenth >= 65536: writeBuf :=make([]byte,10) writeBuf[framePos] = b0 writeBuf[framePos+1] = b1 | 127 binary.BigEndian.PutUint64(writeBuf[framePos+2:], uint64(lenth)) return BytesCombine(writeBuf,data) case lenth > 125: fmt.Println("》125") writeBuf :=make([]byte,4) writeBuf[framePos] = b0 writeBuf[framePos+1] = b1 | 126 binary.BigEndian.PutUint16(writeBuf[framePos+2:], uint16(lenth)) fmt.Println(writeBuf) return BytesCombine(writeBuf,data) default: writeBuf :=make([]byte,2) writeBuf[framePos] = b0 writeBuf[framePos+1] = b1 | byte(lenth) return BytesCombine(writeBuf,data) }
有了上面三个函数,随便写一个TCP服务器,然后使用上述三个函数去和tcp客户端通信,就是websocket服务器了。
实测效果:
效果一级棒。。。。图就不贴了。。。。
题外话。。。
其实POST、GET、websocket、tcp都是tcp协议,在Python上,我曾经编写一个服务器程序,可以同时支持POST、GET、websocket、tcp四种tcp客户端,实用性大大增加