浏览器客户端:

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>websocket测试程序 </title> <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 (ev) { if (ws) { return false; } ws = new WebSocket("ws://127.0.0.1:8888/ws"); ws.onopen = function (ev) { print("连接成功"); }; ws.onclose = function (ev) { print("连接关闭"); }; ws.onerror = function (ev) { print("发生错误 " + ev.data) }; ws.onmessage = function (ev1) { print("服务端消息: " + ev1.data) }; return false }; document.getElementById("send").onclick = function (ev) { if (!ws) { return false } if (input.value !== "") { ws.send(input.value) } else { print("发送内容不能为空") } }; document.getElementById("close").onclick = function (ev) { if (ws) { ws.close() } } }) </script></head><body><div> <br> websocket测试程序,消息又客户端发送到server然后原封不动的返回,server使用go实现 <br> <br> <br> <input type="button" value="连接" id="open"> <input placeholder="输入要发送的消息..." id="input"> <input type="button" value="发送" id="send"> <input type="button" value="关闭" id="close"></div><div id="output"></div></body></html>


版本一:

package mainimport ( "net/http" "github.com/gorilla/websocket")var ( // http升级websocket协议的配置 upgrader = websocket.Upgrader{ // 允许所有CORS跨域请求 CheckOrigin: func(r *http.Request) bool { return true }, })func wsHandler(writer http.ResponseWriter, request *http.Request) { var ( conn *websocket.Conn err error //msgType int data []byte ) //完成握手应答 if conn, err = upgrader.Upgrade(writer, request, nil); err != nil { return } //数据收发 for { //数据类型有text、binary,此处选text if _, data, err = conn.ReadMessage(); err != nil { goto ERR } if err = conn.WriteMessage(websocket.TextMessage, data); err != nil { goto ERR } }ERR: conn.Close()}func main() { http.HandleFunc("/ws", wsHandler) http.ListenAndServe("127.0.0.1:8888", nil)}

版本一未做优化

版本二:

connection.go:

package implimport ( "github.com/gorilla/websocket" "sync" "errors")type Connection struct { wsConn *websocket.Conn // 底层websocket inChan chan []byte // 读队列 outChan chan []byte // 写队列 closeChan chan byte // 关闭通知 isClosed bool mutex sync.Mutex // 避免重复关闭管道}//封装websocket长连接func InitConnection(wsConn *websocket.Conn) (conn *Connection, err error) { conn = &Connection{ wsConn: wsConn, inChan: make(chan []byte, 1000), outChan: make(chan []byte, 1000), closeChan: make(chan byte, 1), } //启动读协程 go conn.readLoop() //启动写协程 go conn.writeLoop() return}func (conn *Connection) ReadMessage() (data []byte, err error) { select { case data = <- conn.inChan: case <- conn.closeChan: err = errors.New("connection is closed") } return}func (conn *Connection) WriteMessage(data []byte) (err error) { select { case conn.outChan <- data: case <- conn.closeChan: err = errors.New("connection is closed") } return}func (conn *Connection) Close() { // wsConn.Close是线程安全的,可重入的(可以多次关闭) conn.wsConn.Close() //一个chan只能关闭一次(所以要保证这行代码只执行一次) conn.mutex.Lock() if !conn.isClosed{ close(conn.closeChan) conn.isClosed = true } conn.mutex.Unlock()}//内部实现func (conn *Connection) readLoop() { var ( data []byte err error ) //不停的读 for { if _, data, err = conn.wsConn.ReadMessage(); err != nil { goto ERR } //阻塞在这里,等待inChan有空闲位置 select { case conn.inChan <- data: case <- conn.closeChan: //当closeChan被关闭就进入这个分支 goto ERR } }ERR: conn.Close()}func (conn *Connection) writeLoop() { var ( data []byte err error ) //不停写 for { select { case data = <-conn.outChan: case <- conn.closeChan: goto ERR } if err = conn.wsConn.WriteMessage(websocket.TextMessage, data); err != nil{ goto ERR } }ERR: conn.Close()}

server.go:

package mainimport ( "net/http" "github.com/gorilla/websocket" "./impl" "time")var ( // http升级websocket协议的配置 upgrader = websocket.Upgrader{ // 允许所有CORS跨域请求 CheckOrigin: func(r *http.Request) bool { return true }, })func wsHandler(writer http.ResponseWriter, request *http.Request) { var ( wsConn *websocket.Conn err error //msgType int data []byte conn *impl.Connection ) //完成握手应答 if wsConn, err = upgrader.Upgrade(writer, request, nil); err != nil { return } if conn, err = impl.InitConnection(wsConn); err != nil { goto ERR } // 不停发送心跳信息 go func() { var ( err error ) for { if err = conn.WriteMessage([]byte("heartbeat")); err != nil { return } time.Sleep(time.Second) } }() for { if data, err = conn.ReadMessage(); err != nil { goto ERR } if err = conn.WriteMessage(data); err != nil { goto ERR } }ERR: // 关闭连接的操作 conn.Close() /*//数据收发 for { //数据类型有text、binary,此处选text if _, data, err = conn.ReadMessage(); err != nil { goto ERR } if err = conn.WriteMessage(websocket.TextMessage, data); err != nil { goto ERR } }ERR: conn.Close()*/}func main() { http.HandleFunc("/ws", wsHandler) http.ListenAndServe("127.0.0.1:8888", nil)}

做了封装,线程安全