设为首页 收藏本站
查看: 529|回复: 0

[经验分享] GIN+GORILLA=A GOLANG WEBSOCKET SERVER

[复制链接]

尚未签到

发表于 2018-9-19 13:48:31 | 显示全部楼层 |阅读模式
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) bool { return true }
  
conn, err := wsh.wsupgrader.Upgrade(w, r, nil)
  
if err != nil {
  
logger.Error("Failed to set websocket upgrade: " + err.Error())
  
return
  
}
  
defer conn.Close()
  
if ctx, err := wsh.onOpen(conn, r); err != nil {
  
logger.Error("Open connection failed " + err.Error() + r.URL.RawQuery)
  
return
  
} else {
  
conn.SetPingHandler(func(message string) error {
  
conn.WriteControl(websocket.PongMessage, []byte(message), time.Now().Add(time.Second))
  
return nil
  
})
  
for {
  
t, msg, err := conn.ReadMessage()
  
if err != nil {
  
logger.Error("READ ERR FROM " + ctx.String() + " ERR " + err.Error())
  
wsh.onClose(conn, ctx)
  
return
  
}
  

  
switch t {
  
case websocket.TextMessage, websocket.BinaryMessage:
  
wsh.onMessage(conn, ctx, msg, t)
  
case websocket.CloseMessage:
  
wsh.onClose(conn, ctx)
  
return
  
case websocket.PingMessage:
  
case websocket.PongMessage:
  
}
  

  
}
  
}
  
}
  

  
func (wsh *WebSocketHandler) closeConnWithCtx(ctx *ConnContext) {
  
keyString := ctx.AsHashKey()
  
ctxHashMap.Remove(keyString)
  
return
  
}
  
func (wsh *WebSocketHandler) processIncomingTextMsg(conn *websocket.Conn, ctx *ConnContext, msg []byte) {
  
logger.Debug("CLIENT SAID " + string(msg))
  
sendMessageToAll(msg)
  
}
  

  
func (wsh *WebSocketHandler) sendMessageToAll(msg []byte]) {
  
var gzMsg bytes.Buffer
  
gzWriter := gzip.NewWriter(&gzMsg)
  
gzWriter.Write(msg)
  
gzWriter.Flush()
  
gzWriter.Close()
  
for key, conn := range ctxHashMap.Items() {
  
if ctx, err := HashKeyAsCtx(key.(string)); err != nil {
  
wsh.onError(err.Error())
  
} else {
  
if ctx.supportGzip == "1" {
  
err = conn.(*websocket.Conn).WriteMessage(websocket.BinaryMessage, gzMsg.Bytes())
  
logger.Debug("send binary msg to " + ctx.String())
  
} else {
  
err = conn.(*websocket.Conn).WriteMessage(websocket.TextMessage, []byte(msg))
  
logger.Debug("send text msg to " + ctx.String())
  
}
  
if err != nil {
  
wsh.onClose(conn.(*websocket.Conn), ctx)
  
conn.(*websocket.Conn).Close()
  
wsh.onError("WRITE ERR TO " + key.(string) + " ERR:" + err.Error())
  
}
  
}
  
}
  

  
}



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-594332-1-1.html 上篇帖子: Golang学习 - path/filepath 包 下篇帖子: Golang 工程自管理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表