go-WebSocket编程

WebSocket是HTML5的重要特性,它实现了基于浏览器的远程socket,它使浏览器和服务器可以进行全双工通信。

在WebSocket出发之前,为了实现及时通信,采用的技术都是“轮询”,即在特定的时间间隔内,由浏览器对服务器发出HTTP Request,服务器在收到请求后,返回最新的数据给浏览器刷新,“轮询”使得浏览器需要对服务器不断发出请求,这样会占用大量的带宽。

WebSocket采用了一些特殊的报头,使得浏览器和服务器只需要做一个握手的动作,就可以在浏览器和服务器之间建立一条连接通道。且此连接会保持在活动状态,可以使用JavaScript来向连接写入或从中接收数据。相比传统的HTTP有如下好处:

  • 一个Web客户端只建立一个TCP连接
  • WebSocket服务端可以推送数据到web客户端
  • 有更加轻量级的头,减少数据传输量

1.WebSocket客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<h1>Websocket Test</h1>
<form>
<p>
message: <input type="text" id="message" value="hello world!">
</p>
</form>
<button onclick="send();">Send Message</button>
</body>
<script type="text/javascript">
var sock = null;
var wsuri = "ws://127.0.0.1:8080";

window.onload = function () {
console.log("onload");
sock = new WebSocket(wsuri);

sock.onopen = function () {
console.log("connected to " + wsuri)
};

sock.onclose = function (e) {
console.log("connection closed (" + e.code + ")")
};

sock.onmessage = function (e) {
console.log("message received: " + e.data)
};
};

function send() {
var msg = document.getElementById('message').value;
sock.send(msg)
}
</script>

</html>

2.WebSocket服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import (
"log"
"net/http"

"github.com/gorilla/websocket"
)

func main() {
// 注册路由并启动服务
http.HandleFunc("/", handleWS)
http.ListenAndServe(":8080", nil)
}

var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
// 处理跨域问题,这里不做任何处理,直接返回true
CheckOrigin: func(r *http.Request) bool {
return true
},
}

func handleWS(w http.ResponseWriter, r *http.Request) {
// 将http请求升级为WebSocket请求
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatal(err)
return
}
defer conn.Close()

// 处理请求
for {
// 从连接中读取数据
messageType, data, err := conn.ReadMessage()
if err != nil {
log.Fatal(err)
return
}

// 向连接中发送数据
resp := []byte("recive data, and the data is " + string(data))
err = conn.WriteMessage(messageType, resp)
if err != nil {
log.Fatal(err)
return
}
}
}