WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。
WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。
在golang语言中,目前有两种比较常用的实现方式:一个是golang自带的库,另一个是gorilla,功能强大。
golang自带库的使用例子可参考以前的博文:Golang如何使用websocket
本文以gorilla为例,介绍websocket的使用。
下载gorilla
# go get github.com/gorilla/websocket
下面例子中主要包括两部分,server和client。
client部分又包括:web client(浏览器)和非web client。
server
server端是一个HTTP 服务器,监听8080端口。
当接收到连接请求后,将连接使用的http协议升级为websocket协议。后续通信过程中,使用websocket进行通信。
对每个连接,server端等待读取数据,读到数据后,打印数据,然后,将数据又发送给client.
server启动方式
# go run server.go
server.go代码如下:
// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// +build ignorepackage mainimport ( ???"flag" ???"html/template" ???"log" ???"net/http" ???"github.com/gorilla/websocket")var addr = flag.String("addr", "localhost:8080", "http service address")var upgrader = websocket.Upgrader{} // use default optionsfunc echo(w http.ResponseWriter, r *http.Request) { ???c, err := upgrader.Upgrade(w, r, nil) ???if err != nil { ???????log.Print("upgrade:", err) ???????return ???} ???defer c.Close() ???for { ???????mt, message, err := c.ReadMessage() ???????if err != nil { ???????????log.Println("read:", err) ???????????break ???????} ???????log.Printf("recv: %s", message) ???????err = c.WriteMessage(mt, message) ???????if err != nil { ???????????log.Println("write:", err) ???????????break ???????} ???}}func home(w http.ResponseWriter, r *http.Request) { ???homeTemplate.Execute(w, "ws://"+r.Host+"/echo")}func main() { ???flag.Parse() ???log.SetFlags(0) ???http.HandleFunc("/echo", echo) ???http.HandleFunc("/", home) ???log.Fatal(http.ListenAndServe(*addr, nil))}var homeTemplate = template.Must(template.New("").Parse(`<!DOCTYPE html><html><head><meta charset="utf-8"><script> ?window.addEventListener("load", function(evt) { ???var output = document.getElementById("output"); ???var input = document.getElementById("input"); ???var ws; ???var print = function(message) { ???????var d = document.createElement("div"); ???????d.innerHTML = message; ???????output.appendChild(d); ???}; ???document.getElementById("open").onclick = function(evt) { ???????if (ws) { ???????????return false; ???????} ???????ws = new WebSocket("{{.}}"); ???????ws.onopen = function(evt) { ???????????print("OPEN"); ???????} ???????ws.onclose = function(evt) { ???????????print("CLOSE"); ???????????ws = null; ???????} ???????ws.onmessage = function(evt) { ???????????print("RESPONSE: " + evt.data); ???????} ???????ws.onerror = function(evt) { ???????????print("ERROR: " + evt.data); ???????} ???????return false; ???}; ???document.getElementById("send").onclick = function(evt) { ???????if (!ws) { ???????????return false; ???????} ???????print("SEND: " + input.value); ???????ws.send(input.value); ???????return false; ???}; ???document.getElementById("close").onclick = function(evt) { ???????if (!ws) { ???????????return false; ???????} ???????ws.close(); ???????return false; ???};});</script></head><body><table><tr><td valign="top" width="50%"><p>Click "Open" to create a connection to the server, "Send" to send a message to the server and "Close" to close the connection. You can change the message and send multiple times.<p><form><button id="open">Open</button><button id="close">Close</button><p><input id="input" type="text" value="Hello world!"><button id="send">Send</button></form></td><td valign="top" width="50%"><div id="output"></div></td></tr></table></body></html>`))
server output:
recv: 2018-10-20 17:09:12.497129965 +0800 CST m=+1.010137585
recv: 2018-10-20 17:09:13.495602484 +0800 CST m=+2.008641088
recv: 2018-10-20 17:09:14.49401062 +0800 CST m=+3.007080206
recv: 2018-10-20 17:09:15.497114615 +0800 CST m=+4.010215329
recv: 2018-10-20 17:09:16.494394706 +0800 CST m=+5.007526368
read: websocket: close 1000 (normal)
非web client
client启动后,首先连接server。
连接建立后,主routine每一秒钟向server发送消息(当前时间)。
另一个routine从server接收数据,并打印。
当client退出时,会向server发送关闭消息。接着,等待退出。
client启动方式
# go run client.go
client代码如下:
// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// +build ignorepackage mainimport ( ???"flag" ???"log" ???"net/url" ???"os" ???"os/signal" ???"time" ???"github.com/gorilla/websocket")var addr = flag.String("addr", "localhost:8080", "http service address")func main() { ???flag.Parse() ???log.SetFlags(0) ???interrupt := make(chan os.Signal, 1) ???signal.Notify(interrupt, os.Interrupt) ???u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"} ???log.Printf("connecting to %s", u.String()) ???c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) ???if err != nil { ???????log.Fatal("dial:", err) ???} ???defer c.Close() ???done := make(chan struct{}) ???go func() { ???????defer close(done) ???????for { ???????????_, message, err := c.ReadMessage() ???????????if err != nil { ???????????????log.Println("read:", err) ???????????????return ???????????} ???????????log.Printf("recv: %s", message) ???????} ???}() ???ticker := time.NewTicker(time.Second) ???defer ticker.Stop() ???for { ???????select { ???????case <-done: ???????????return ???????case t := <-ticker.C: ???????????err := c.WriteMessage(websocket.TextMessage, []byte(t.String())) ???????????if err != nil { ???????????????log.Println("write:", err) ???????????????return ???????????} ???????case <-interrupt: ???????????log.Println("interrupt") ???????????// Cleanly close the connection by sending a close message and then ???????????// waiting (with timeout) for the server to close the connection. ???????????err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) ???????????if err != nil { ???????????????log.Println("write close:", err) ???????????????return ???????????} ???????????select { ???????????case <-done: ???????????case <-time.After(time.Second): ???????????} ???????????return ???????} ???}}
client output:
connecting to ws://localhost:8080/echo
recv: 2018-10-20 17:09:12.497129965 +0800 CST m=+1.010137585
recv: 2018-10-20 17:09:13.495602484 +0800 CST m=+2.008641088
recv: 2018-10-20 17:09:14.49401062 +0800 CST m=+3.007080206
recv: 2018-10-20 17:09:15.497114615 +0800 CST m=+4.010215329
recv: 2018-10-20 17:09:16.494394706 +0800 CST m=+5.007526368
^Cinterrupt
read: websocket: close 1000 (normal)
web client
web client,也就是使用浏览器。
在浏览器中输入http://127.0.0.1:8080
"Open",然后"send"
server output:
recv: Hello world!!
参考
百度百科
https://baike.baidu.com/item/WebSocket
github
https://github.com/gorilla/websocket
doc
https://godoc.org/github.com/gorilla/websocket
example
https://github.com/gorilla/websocket/blob/master/examples/
golang gorilla websocket例子
原文地址:https://www.cnblogs.com/lanyangsh/p/9822403.html