设为首页 加入收藏

TOP

GIN+GORILLA=A GOLANG WEBSOCKET SERVER(一)
2017-09-30 13:54:56 】 浏览:771
Tags:GIN GORILLA GOLANG WEBSOCKET SERVER

   鉴于聊天已然成为大部分app的基础功能,而大部分app用户基数有没有辣么大,常用的聊天server架构如xmpp或者消息队列实现之类的用起来还挺麻烦的,有比较难跟网页端做交互,加之H5标准落地,所以websocket已然成为一个轻巧可用性高的聊天server实现方法;

   websocket的server常见的是用nodejs或者java的netty框架实现,netty相对重一点,direct buffer的内存泄露调起来比较麻烦,试了一下go,轻巧,稳定性不错,性能不错,所以用go实现了一下;

   websocket的协议标准和基本概念网上一搜一片,这里不赘述;

   http server用gin来做,websocket的handler则用gorilla,由于不重复造轮子,所以整个搭建的过程很快;

   

import (
    "util"
    "os"
    "fmt"
    "github.com/DeanThompson/ginpprof"
    "github.com/gin-gonic/gin"
    "runtime"
)
var (
    logger * util.LogHelper
)
func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    logFile,err := os.OpenFile("/var/log/gows.log",os.O_CREATE|os.O_RDWR,0777)
    if err!=nil {
        fmt.Println(err.Error())
        os.Exit(0)
    }
    defer logFile.Close()
    logger = util.NewLogger(logFile)
    logger.Info("Starting system...")
    wsHandler := new(WebSocketHandler)
    gin.SetMode(gin.ReleaseMode)
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
        wsHandler.HandleConn(c.Writer, c.Request)
    })
    ginpprof.Wrapper(r)//调试用 可以看到堆栈状态和所有goroutine状态
    //err = r.Run(listenPath, certPath, keyPath) 这样可以支持wss
    err = r.Run("127.0.0.1:8888")
    if err != nil {
        fmt.Println(err)
    }
}

这样我们的入口就有了~

websocket的模式大概是 onopen onmessage onerror onclose四个callback来覆盖整个通信流程

所以我们来看下简易版本的websockethandler的实现

package main

import (
    "bytes"
    "compress/gzip"
    "encoding/json"
    "errors"
    "net/http"
    "strconv"
    "time"
    "util"
    "github.com/gorilla/websocket"
)

var (
    ctxHashMap  = util.NewConcurrentMap()
)
//用来升级http协议到ws协议
type WebSocketHandler struct {
    wsupgrader websocket.Upgrader
}

func (wsh *WebSocketHandler) NewWebSocketHandler() {
    wsh.wsupgrader = websocket.Upgrader{
        ReadBufferSize:  4096,
        WriteBufferSize: 4096,
    }
}

func (wsh *WebSocketHandler) onMessage(conn *websocket.Conn, ctx *ConnContext, msg []byte, msgType int) {
    //处理文本消息 或者 2进制消息 2进制通常是些 gzip的文本 语音或者图片视频之类的一般会用其他云服务不然带宽会爆掉
    if msgType == websocket.TextMessage {
        wsh.processIncomingTextMsg(conn, ctx, msg)
    }
    if msgType == websocket.BinaryMessage {

    }
}

func (wsh *WebSocketHandler) onOpen(conn *websocket.Conn, r *http.Request) (ctx *ConnContext, err error) {
    if err := r.ParseForm(); err != nil {
        return nil, errors.New("参数校验错误")
    }
    specialKey := r.FormValue("specialKey")
    supportGzip := r.FormValue("support_gzip")
    
    ctx = &ConnContext{specialKey, supportGzip}
    //用来标识一个tcp链接
    keyString := ctx.AsHashKey()

    if oldConn, ok := ctxHashMap.Get(keyString); ok {
            wsh.onClose(oldConn.(*websocket.Conn), ctx)
            oldConn.(*websocket.Conn).Close()
    }
    ctxHashMap.Set(keyString, conn)
    return ctx, nil
}

func (wsh *WebSocketHandler) onClose(conn *websocket.Conn, ctx *ConnContext) {
    logger.Info("client close itself as " + ctx.String())
    wsh.closeConnWithCtx(ctx)
    return
}

func (wsh *WebSocketHandler) onError(errMsg string) {
    logger.Error(errMsg)
}
func (wsh *WebSocketHandler) HandleConn(w http.ResponseWriter, r *http.Request) {
    wsh.wsupgrader.CheckOrigin = func(r *http.Request) b
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇golang-MD5 下一篇godef 插件安装

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

最新文章

热门文章

C 语言

C++基础

windows编程基础

linux编程基础

C/C++面试题目