zoukankan      html  css  js  c++  java
  • 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()
    }
    

      

    效果展示:

  • 相关阅读:
    队列ADT
    使用QQ截图右键菜单
    字对齐、半字对齐、字节对齐的理解
    Linux虚拟机下使用USB转串口线——配置minicom、以及screen的使用
    栈的应用实例——中缀表达式转换为后缀表达式
    使用ADS1.2的注意事项及常用技巧
    VMWare虚拟机“锁定文件失败“怎么办?
    把Linux目录挂载到开发板、设置开发板从NFS启动、取消开发板从NFS启动
    关于交叉开发环境
    JavaWeb学习----JSP脚本元素、指令元素、动作元素
  • 原文地址:https://www.cnblogs.com/lianzhilei/p/11478190.html
Copyright © 2011-2022 走看看