<!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)}
做了封装,线程安全
websocket小案例
原文地址:https://blog.51cto.com/5660061/2361096