Go语言 websocket

websocket消息服务

 

目的:搭建websocket服务,用浏览器与服务进行消息交互(写的第一个Go程序)

 

代码目录结构:

 

前端html页面:

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="utf-8">
 5     <script>
 6         window.addEventListener("load", function(evt) {
 7             var output = document.getElementById("output");
 8             var input = document.getElementById("input");
 9             var ws;
10             var print = function(message) {
11                 var d = document.createElement("div");
12                 d.innerHTML = message;
13                 output.appendChild(d);
14             };
15             document.getElementById("open").onclick = function(evt) {
16                 if (ws) {
17                     return false;
18                 }
19                 ws = new WebSocket("ws://127.0.0.1:7777/ws");
20                 ws.onopen = function(evt) {
21                     print("OPEN");
22                 }
23                 ws.onclose = function(evt) {
24                     print("CLOSE");
25                     ws = null;
26                 }
27                 ws.onmessage = function(evt) {
28                     print("RESPONSE: " + evt.data);
29                 }
30                 ws.onerror = function(evt) {
31                     print("ERROR: " + evt.data);
32                 }
33                 return false;
34             };
35             document.getElementById("send").onclick = function(evt) {
36                 if (!ws) {
37                     return false;
38                 }
39                 print("SEND: " + input.value);
40                 ws.send(input.value);
41                 return false;
42             };
43             document.getElementById("close").onclick = function(evt) {
44                 if (!ws) {
45                     return false;
46                 }
47                 ws.close();
48                 return false;
49             };
50         });
51     </script>
52 </head>
53 <body>
54 <table>
55     <tr><td valign="top" width="50%">
56         <p>Click "Open" to create a connection to the server,
57             "Send" to send a message to the server and "Close" to close the connection.
58             You can change the message and send multiple times.
59         </p>
60             <form>
61                 <button id="open">Open</button>
62                 <button id="close">Close</button>
63             <input id="input" type="text" value="Hello world!">
64             <button id="send">Send</button>
65             </form>
66     </td><td valign="top" width="50%">
67         <div id="output"></div>
68     </td></tr></table>
69 </body>
70 </html>
client.html

 

server.go代码:

package main

import (
	"fmt"
	"github.com/gorilla/websocket"
	"go_websocket"
	"net/http"
)

// http升级websocket协议的配置
var wsUpgrader = websocket.Upgrader{
	// 允许跨域CORS
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// 消息处理
func wsHandler(resp http.ResponseWriter, req *http.Request) {
	wsSocket, err := wsUpgrader.Upgrade(resp, req, nil)
	if err != nil {
		return
	}
	wsConn := go_websocket.WsConnectionInit(wsSocket)
	wsConn.Run()

	for {
		wsmsg, err := wsConn.ReadMessage()
		if err != nil {
			goto error
		}
		err = wsConn.WriteMessage(wsmsg)
		if err != nil {
			goto error
		}
	}
error:
	fmt.Println("websocket is closed")
	return
}

func main() {
	fmt.Println("websocket start")
	http.HandleFunc("/ws", wsHandler)
	http.ListenAndServe("0.0.0.0:7777", nil)
}

 

connection.go代码:

package go_websocket

import (
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"sync"
	"time"
)

// 客户端读写消息
type WsMessage struct {
	msgType int
	data    []byte
}

// 客户端连接
type wsConnection struct {
	wsSocket *websocket.Conn
	inChan   chan *WsMessage
	outChan  chan *WsMessage

	isClosed  bool
	closeChan chan []byte
	mutex     sync.Mutex
}

// 连接初始化
func WsConnectionInit(wsSocket *websocket.Conn) (wsConn *wsConnection) {
	wsConn = &wsConnection{
		wsSocket:  wsSocket,
		inChan:    make(chan *WsMessage, 1000),
		outChan:   make(chan *WsMessage, 1000),
		closeChan: make(chan []byte, 1),
	}
	return wsConn
}

// 启动
func (wsConn *wsConnection) Run() {
	go wsConn.readLoop()
	go wsConn.writeLoop()
	go wsConn.heartbeat()
}

// 心跳检测
func (wsConn *wsConnection) heartbeat() {
	for {
		time.Sleep(2 * time.Second)
		wsmsg := &WsMessage{msgType: websocket.TextMessage, data: []byte("heartbeat")}
		err := wsConn.WriteMessage(wsmsg)
		if err != nil {
			fmt.Println("send heartbeat stop")
			return
		}
	}
}

// 循环接收
func (wsConn *wsConnection) readLoop() {
	var ()

	for {
		msgType, data, err := wsConn.wsSocket.ReadMessage()
		if err != nil {
			goto error
		}
		select {
		case wsConn.inChan <- &WsMessage{msgType: msgType, data: data}:
		case <-wsConn.closeChan:
			goto closed
		}
	}
error:
	wsConn.Close()
closed:
	fmt.Println("readLoop closed")
}

// 循环发送
func (wsConn *wsConnection) writeLoop() {
	for {
		select {
		case wsmsg := <-wsConn.outChan:
			if err := wsConn.wsSocket.WriteMessage(wsmsg.msgType, wsmsg.data); err != nil {
				goto error
			}
		case <-wsConn.closeChan:
			goto closed
		}
	}
error:
	wsConn.Close()
closed:
	fmt.Println("writeLoop close")
}

// 取消息,外部可调用
func (wsConn *wsConnection) ReadMessage() (wsmsg *WsMessage, err error) {
	select {
	case wsmsg = <-wsConn.inChan:
		return wsmsg, nil
	case <-wsConn.closeChan:
		return nil, errors.New("websocket is closed")
	}
}

// 写消息,外部可调用
func (wsConn *wsConnection) WriteMessage(wsmsg *WsMessage) (err error) {
	select {
	case wsConn.outChan <- wsmsg:
	case <-wsConn.closeChan:
		return errors.New("websocket is closed")
	}
	return nil
}

// 关闭wsSocket
func (wsConn *wsConnection) Close() {
	wsConn.wsSocket.Close()

	// 加锁
	wsConn.mutex.Lock()
	if !wsConn.isClosed {
		wsConn.isClosed = true
		close(wsConn.closeChan)
	}
	wsConn.mutex.Unlock()
}

  

效果展示:

 

posted @ 2019-09-06 20:52  Assassinの  阅读(1294)  评论(0编辑  收藏  举报