参考
WebSocket是HTML5的重要特性,它实现了基于浏览器的远程socket,它使浏览器和服务器可以进行全双工通信,许多浏览器(Firefox、Google Chrome和Safari)都已对此做了支持。
WebSocket采用了一些特殊的报头,使得浏览器和服务器只需要做一个握手的动作,就可以在浏览器和服务器之间建立一条连接通道。且此连接会保持在活动状态,它解决了Web实时化的问题,Websocket服务端可以推送(push)数据到web客户端。
WebSocket URL的起始输入是ws://或是wss://(在SSL上)。下图展示了WebSocket的通信过程,一个带有特定报头的HTTP握手被发送到了服务器端,接着在服务器端或是客户端就可以通过JavaScript来使用某种套接口(socket),这一套接口可被用来通过事件句柄异步地接收数据。
示例1
对参考进行了简洁化和优化,自己写了测试代码,浏览器演示如下
服务端
package main
import (
"fmt"
"golang.org/x/net/websocket"
"html/template"
"io"
"log"
"net/http"
)
func Copy(ws *websocket.Conn) {
fmt.Printf("copyServer %#v\n", ws.Config())
io.Copy(ws, ws) //websocket.Conn实现了Read()和Write(),所以可以直接Copy。此时会阻塞并不断复制返回接收到的内容
fmt.Println("copyServer finished")
}
func ReadWrite(ws *websocket.Conn) {
fmt.Printf("readWriteServer %#v\n", ws.Config())
buf := make([]byte, 100)
for {
n, err := ws.Read(buf)
if err != nil {
fmt.Println(err)
break
}
fmt.Printf("recv:%q\n", buf[:n])
n, err = ws.Write(buf[:n])
if err != nil {
fmt.Println(err)
break
}
fmt.Printf("send:%q\n", buf[:n])
}
fmt.Println("readWriteServer finished")
}
// 当接收string时,websocket.Message.Receive(ws, &buf)和ws.Read(buf)功能类似。发送同理
func RecvSend(ws *websocket.Conn) {
fmt.Printf("recvSendServer %#v\n", ws)
for {
var buf string
err := websocket.Message.Receive(ws, &buf)
if err != nil {
fmt.Println(err)
break
}
fmt.Printf("recv:%q\n", buf)
err = websocket.Message.Send(ws, buf)
if err != nil {
fmt.Println(err)
break
}
fmt.Printf("send:%q\n", buf)
}
fmt.Println("recvSendServer finished")
}
// 当接收[]byte时,websocket.Message.Receive(ws, &buf)可以接收发送二进制文件
func RecvSendBinary(ws *websocket.Conn) {
fmt.Printf("recvSendBinaryServer %#v\n", ws)
for {
var buf []byte
err := websocket.Message.Receive(ws, &buf)
if err != nil {
fmt.Println(err)
break
}
fmt.Printf("recv:%#v\n", buf)
err = websocket.Message.Send(ws, buf)
if err != nil {
fmt.Println(err)
break
}
fmt.Printf("send:%#v\n", buf)
}
fmt.Println("recvSendBinaryServer finished")
}
type T struct {
Msg string `json:"msg"`
Path string `json:"path"`
}
func Json(ws *websocket.Conn) {
fmt.Printf("jsonServer %#v\n", ws.Config())
for {
var msg T
err := websocket.JSON.Receive(ws, &msg)
if err != nil {
fmt.Println(err)
break
}
fmt.Printf("recv:%#v\n", msg)
err = websocket.JSON.Send(ws, msg)
if err != nil {
fmt.Println(err)
break
}
fmt.Printf("send:%#v\n", msg)
}
fmt.Println("jsonServer finished")
}
func web(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("main/websocket.html")
t.Execute(w, nil)
}
func main() {
//接收websocket的路由地址
http.Handle("/copy", websocket.Handler(Copy))
http.Handle("/readWrite", websocket.Handler(ReadWrite))
http.Handle("/recvSend", websocket.Handler(RecvSend))
http.Handle("/recvSendBinary", websocket.Handler(RecvSendBinary))
http.Handle("/json", websocket.Handler(Json))
//html页面
http.HandleFunc("/web", web)
if err := http.ListenAndServe(":1234", nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
HTML文件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>websocket test</title>
</head>
<body>
<h1>select method</h1>
<form>
<input type="radio" name="route" value="ws://127.0.0.1:1234/copy">copy<br>
<input type="radio" name="route" value="ws://127.0.0.1:1234/readWrite">readWrite<br>
<input type="radio" name="route" value="ws://127.0.0.1:1234/recvSend">recvSend<br>
<input type="radio" name="route" value="ws://127.0.0.1:1234/recvSendBinary">recvSendBinary<br>
<input type="radio" name="route" value="ws://127.0.0.1:1234/json">json<br>
</form>
<button onclick="connect()">连接</button>
<button onclick="disconnect()">断开</button>
<h1>send</h1>
<input id="message" type="text" value="Hello, world!">
<button onclick="send()">Send Message</button>
<h1>receive</h1>
<input id="receive" readonly>
</body>
<script type="text/javascript">
let sock = null;
function connect() {
let url = null;
let routes = document.getElementsByName("route");
for (let j = 0; j < routes.length; j++){
if (routes[j].checked){
url = routes[j].value
}
if (url === "ws://127.0.0.1:1234/json"){
document.getElementById('message').value = JSON.stringify({msg: 'Hello', path: 'localhost'})
}
}
if (url != null){
console.log("url:" + url);
sock = new WebSocket(url);
sock.onopen = function () {
console.log("connected to " + url);
};
sock.onmessage = function (e) {
document.getElementById('receive').value = e.data;
};
sock.onclose = function (e) {
console.log("connection: " + url + " closed(" + e.code + ")")
};
}
}
function disconnect() {
sock.close();
}
function send() {
let msg = document.getElementById('message').value;
sock.send(msg);
}
</script>
</html>
示例2
使用go版本客户端和服务器通讯,不断地相互发送消息
服务端
package main
import (
"fmt"
"golang.org/x/net/websocket"
"net/http"
"strconv"
"time"
)
func EchoServer(ws *websocket.Conn) {
go func() {
buf := make([]byte, 100)
for{
n, err := ws.Read(buf)
if err != nil {
fmt.Println(err)
break
}
fmt.Println("receive: ", string(buf[:n]))
}
}()
var send int
for {
sendStr := strconv.Itoa(send)
_, err := ws.Write([]byte(sendStr))
if err != nil {
fmt.Println(err)
break
}
fmt.Println("send: ", sendStr)
time.Sleep(time.Second)
send++
}
}
func main() {
http.Handle("/echo", websocket.Handler(EchoServer))
err := http.ListenAndServe(":12345", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}
//output
//send: 0
//receive: 0
//send: 1
//receive: 1
//send: 2
//send: 3
//receive: 2
//send: 4
客户端
package main
import (
"golang.org/x/net/websocket"
"log"
"strconv"
"time"
"fmt"
)
func main() {
origin := "http://localhost/" //客户端地址
url := "ws://localhost:12345/echo" //服务器地址
ws, err := websocket.Dial(url, "", origin) //第二个参数是websocket子协议,可以为空
if err != nil {
log.Fatal(err)
}
go func() {
var send int
for {
sendStr := strconv.Itoa(send)
_, err := ws.Write([]byte(sendStr))
if err != nil {
log.Fatal(err)
}
time.Sleep(time.Second*2)
send++
}
}()
var buf= make([]byte, 100)
for {
n, err := ws.Read(buf)
if err != nil {
log.Fatal(err)
}
fmt.Println("receive: ", string(buf[:n]))
}
}
//output
//receive: 0
//receive: 1
//receive: 2
//receive: 3
//receive: 4
//receive: 5
//receive: 6