参考

 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