简介
在 GoLang 中可以通过 golang.org/x/net/websocket
包使用 WebSocket 功能,如下是一个简单的示例,包含了两个处理 URI ,其中 /
会返回主页的模板,然后通过 /upper
将字符串转换为大写。
package main
import (
"fmt"
"html/template"
"net/http"
"os"
"strings"
"golang.org/x/net/websocket"
)
func upper(ws *websocket.Conn) {
var err error
for {
var reply string
if err = websocket.Message.Receive(ws, &reply); err != nil {
fmt.Println(err)
break
}
if err = websocket.Message.Send(ws, strings.ToUpper(reply)); err != nil {
fmt.Println(err)
break
}
}
}
func index(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
return
}
t, _ := template.ParseFiles("index.html")
t.Execute(w, nil)
}
func main() {
http.Handle("/upper", websocket.Handler(upper))
http.HandleFunc("/", index)
if err := http.ListenAndServe(":9999", nil); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
对应的 HTML 模板 index.html
为。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Websocket</title>
</head>
<body>
<h1>String to Upper</h1>
<form><p>Input : <input id="content" type="text" placeholder="Some String Here."></p></form>
<label id="result">Result:</label><br><br>
<button onclick="send()">Change</button>
<script type="text/javascript">
var sock = new WebSocket("ws://127.0.0.1:9999/upper");
sock.onmessage = function(e) {
var result = document.getElementById('result');
result.innerHTML = "Result:" + e.data;
}
function send() {
var msg = document.getElementById('content').value;
sock.send(msg);
}
</script>
</body>
</html>
参数配置
TLS/SSL
与大多数的 GoLang 网络编程类似,在监听网络端口时,可以选择是否使用 TLS ,两种方式如下。
log.Fatal(http.ListenAndServe(":8080", nil))
log.Fatal(http.ListenAndServeTLS(":8080", "../pki/SVR/cert.pem", "../pki/SVR/key.pem", nil))
通过 log.Fatal
可以在发生异常的时候直接退出。
修改参数
最常见的参数如读写缓冲区,如下代码中,会通过 upgrader.Upgrade()
将协议从 HTTP 切换到 WebSocket ,而这里的 upgrader
实际上就是通过如下代码定义的,其中包含了具体的配置参数。
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
定制化
有时候在切换到 WebSocket 协议之前,我们希望通过最初的 HTTP 协议再获取一些参数信息,例如检查是否同源 (checkSameOrigin)、子协议的版本号 (selectSubprotocol)、鉴权信息等等,也就是在转换前进行检查。
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func ServeWebSocket(w http.ResponseWriter, r *http.Request) {
hdr := http.Header{}
subproto := r.Header.Get("Sec-Websocket-Protocol")
log.Printf("got websocket sub-protocol '%s'.\n", subproto)
for _, name := range websocket.Subprotocols(r) {
log.Printf("Got subprotocols '%s'.\n", name);
// check and choose a protocol.
hdr.Set("Sec-Websocket-Protocol", name);
break
}
ws, err := upgrader.Upgrade(w, r, hdr)
if err != nil {
log.Println("upgrade websocket failed,", err)
return
}
defer ws.Close()
log.Println("got websocket connection from", ws.RemoteAddr())
ws.SetPingHandler(cli.pingHandler)
// handle your own business.
}
func main() {
http.HandleFunc("/echo", ServeWebSocket)
//log.Fatal(http.ListenAndServe(":8080", nil))
log.Fatal(http.ListenAndServeTLS(":8080", "../pki/SVR/cert.pem", "../pki/SVR/key.pem", nil))
}
在处理完后,通过 upgrader.Upgrade()
升级到 WebSocket 协议。
Ping
如上,可以通过 ws.SetPingHandler(pingHandler)
注册 Ping 的回调函数,该函数定义为。
func pingHandler(s string) error {
log.Println("get ping", s)
if err := c.ws.WriteMessage(websocket.TextMessage, []byte("string")); err != nil {
return err
}
if err := c.ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
return err
}
return nil
}
注意,上述的方式会发送两个报文。