zoukankan      html  css  js  c++  java
  • golang websocket连接

    1.路由信息

    // 应答客户端告知升级连接为websocket
    beego.Router("/ws", &controllers.WebsocketController{}, "get:WsHandler")
    

    2.Controller实现

    package controllers
    
    import (
    	"errors"
    	"fmt"
    	"net/http"
    	"sync"
    	"time"
    
    	"github.com/astaxie/beego"
    
    	"github.com/gorilla/websocket"
    )
    
    type WebsocketController struct {
    	beego.Controller
    }
    
    var wsUpgrader = websocket.Upgrader{
    	ReadBufferSize:    4096,
    	WriteBufferSize:   4096,
    	EnableCompression: true,
    	HandshakeTimeout:  5 * time.Second,
    	// CheckOrigin: 处理跨域问题,线上环境慎用
    	CheckOrigin: func(r *http.Request) bool {
    		return true
    	},
    }
    
    // 客户端读写消息
    type wsMessage struct {
    	messageType int
    	data        []byte
    }
    
    // 客户端连接
    type wsConnection struct {
    	wsSocket *websocket.Conn // 底层websocket
    	inChan   chan *wsMessage // 读队列
    	outChan  chan *wsMessage // 写队列
    
    	mutex     sync.Mutex // Mutex互斥锁,避免重复关闭管道
    	isClosed  bool
    	closeChan chan byte // 关闭通知
    }
    
    func (wsConn *wsConnection) wsReadLoop() {
    	for {
    		// 读一个message
    		msgType, data, err := wsConn.wsSocket.ReadMessage()
    		if err != nil {
    			goto error
    		}
    		req := &wsMessage{}
    		if string(data) == "test" {
    			req = &wsMessage{
    				msgType,
    				[]byte("Hi, this is a test websocket"),
    			}
    		} else {
    			req = &wsMessage{
    				msgType,
    				data,
    			}
    		}
    
    		// 放入请求队列
    		select {
    		case wsConn.inChan <- req:
    		case <-wsConn.closeChan:
    			goto closed
    		}
    	}
    error:
    	wsConn.wsClose()
    closed:
    	fmt.Println("websocket is closed.")
    }
    
    func (wsConn *wsConnection) wsWriteLoop() {
    	for {
    		select {
    		// 取一个应答
    		case msg := <-wsConn.outChan:
    			// 写给websocket
    			if err := wsConn.wsSocket.WriteMessage(msg.messageType, msg.data); err != nil {
    				goto error
    			}
    		case <-wsConn.closeChan:
    			goto closed
    		}
    	}
    error:
    	wsConn.wsClose()
    closed:
    	fmt.Println("websocket is closed.")
    }
    
    func (wsConn *wsConnection) procLoop() {
    	// 启动一个gouroutine发送心跳
    	go func() {
    		for {
    			time.Sleep(3 * time.Second)
    			if err := wsConn.wsWrite(websocket.TextMessage, []byte("heartbeat from server")); err != nil {
    				fmt.Println("heartbeat fail")
    				wsConn.wsClose()
    				break
    			}
    		}
    	}()
    
    	for {
    		msg, err := wsConn.wsRead()
    		if err != nil {
    			fmt.Println("read fail")
    			break
    		}
    		fmt.Println(string(msg.data))
    		err = wsConn.wsWrite(msg.messageType, msg.data)
    		if err != nil {
    			fmt.Println("write fail")
    			break
    		}
    	}
    }
    
    func (w *WebsocketController) WsHandler() {
    	wsSocket, err := wsUpgrader.Upgrade(w.Ctx.ResponseWriter, w.Ctx.Request, nil)
    	if err != nil {
    		return
    	}
    	wsConn := &wsConnection{
    		wsSocket:  wsSocket,
    		inChan:    make(chan *wsMessage, 1000),
    		outChan:   make(chan *wsMessage, 1000),
    		closeChan: make(chan byte),
    		isClosed:  false,
    	}
    
    	// 处理器
    	go wsConn.procLoop()
    	// 读协程
    	go wsConn.wsReadLoop()
    	// 写协程
    	go wsConn.wsWriteLoop()
    }
    
    func (wsConn *wsConnection) wsWrite(messageType int, data []byte) error {
    	select {
    	case wsConn.outChan <- &wsMessage{messageType, data}:
    	case <-wsConn.closeChan:
    		return errors.New("websocket closed")
    	}
    	return nil
    }
    
    func (wsConn *wsConnection) wsRead() (*wsMessage, error) {
    	select {
    	case msg := <-wsConn.inChan:
    		return msg, nil
    	case <-wsConn.closeChan:
    	}
    	return nil, errors.New("websocket closed")
    }
    
    func (wsConn *wsConnection) wsClose() {
    	wsConn.wsSocket.Close()
    	wsConn.mutex.Lock()
    	defer wsConn.mutex.Unlock()
    	if !wsConn.isClosed {
    		wsConn.isClosed = true
    		close(wsConn.closeChan)
    	}
    }
    
    
  • 相关阅读:
    Mybatisplus<一> Springboot框架使用MybatisPlus代码自动生成器
    今日收获
    今日收获
    字典特征提取
    sklearn数据集的导入及划分
    文本特征提取
    MySQL基础笔记
    docker笔记
    BOM 中的location对象和history对象
    完善 原生Js 实现的简单无缝滚动轮播图
  • 原文地址:https://www.cnblogs.com/tomtellyou/p/12915502.html
Copyright © 2011-2022 走看看