Golang websocket推送

在工作用主要使用的是Java,也做过IM(后端用的netty websocket)。最近想通过Golang重写下,于是通过websocket撸了一个聊天室。

项目地址

Github

依赖

golang.org/x/net下的websocket。

由于我使用的是golang版本是1.12,在国内访问golang.org/x需要借助代理,或者通过replace替换为github下的镜像。

module github.com/xuanbo/pusher

require golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3

replace (
golang.org/x/crypto => github.com/golang/crypto v0.0.0-20190308221718-c2843e01d9a2
golang.org/x/net => github.com/golang/net v0.0.0-20190404232315-eb5bcb51f2a3
golang.org/x/sys => github.com/golang/sys v0.0.0-20190215142949-d0b11bdaac8a
golang.org/x/text => github.com/golang/text v0.3.0
)

即工程下的go.mod.cn文件。

websocket用法

核心就是for循环下的处理收到的消息逻辑,然后对消息进行处理(转发、广播等)。

// websocket Handler
// usage: http.Handle("/websocket", websocket.Handler(pusher.Handler))
func Handler(conn *websocket.Conn) {
// handle connected
var userId string
var err error
if userId, err = doConnected(conn); err != nil {
fmt.Println("Client connect error: ", err)
return
} fmt.Println("Client connected, userId: ", userId) for {
msg := new(Message) if err := websocket.JSON.Receive(conn, msg); err != nil {
fmt.Println("Can't receive, error: ", err)
break
} msg.UpdateAt = Timestamp() fmt.Println("Received from client: ", msg) // handle received message
if err := doReceived(conn, msg); err != nil {
fmt.Println("Received message error: ", err)
break
}
} // handle disConnected
if err := doDisConnected(userId, conn); err != nil {
fmt.Println("Client disconnected error: ", err)
return
} fmt.Println("Client disconnected, userId: ", userId)
}

连接管理

在IM中比较重要的点就是管理客户端连接,这样我们才能通过服务端转发消息给对应的用户。注意,下面没有考虑集群,只在单机中考虑。

// websocket connection manager
type ConnManager struct {
// websocket connection number
Online *int32
// websocket connection
connections *sync.Map
}

上面定义了一个连接管理结构体,Online为在线的人数,connections为客户端的连接管理(key为userId,value为websocket connection)。

下面为ConnManager添加一些方法来处理连接、断开连接、发送消息、广播等操作。

// add websocket connection
// online number + 1
func (m *ConnManager) Connected(k, v interface{}) {
m.connections.Store(k, v) atomic.AddInt32(m.Online, 1)
} // remove websocket connection by key
// online number - 1
func (m *ConnManager) DisConnected(k interface{}) {
m.connections.Delete(k) atomic.AddInt32(m.Online, -1)
} // get websocket connection by key
func (m *ConnManager) Get(k interface{}) (v interface{}, ok bool) {
return m.connections.Load(k)
} // iter websocket connections
func (m *ConnManager) Foreach(f func(k, v interface{})) {
m.connections.Range(func(k, v interface{}) bool {
f(k, v)
return true
})
} // send message to one websocket connection
func (m *ConnManager) Send(k string, msg *Message) {
v, ok := m.Get(k)
if ok {
if conn, ok := v.(*websocket.Conn); ok {
if err := websocket.JSON.Send(conn, msg); err != nil {
fmt.Println("Send msg error: ", err)
}
} else {
fmt.Println("invalid type, expect *websocket.Conn")
}
} else {
fmt.Println("connection not exist")
}
} // send message to multi websocket connections
func (m *ConnManager) SendMulti(keys []string, msg interface{}) {
for _, k := range keys {
v, ok := m.Get(k)
if ok {
if conn, ok := v.(*websocket.Conn); ok {
if err := websocket.JSON.Send(conn, msg); err != nil {
fmt.Println("Send msg error: ", err)
}
} else {
fmt.Println("invalid type, expect *websocket.Conn")
}
} else {
fmt.Println("connection not exist")
}
}
} // broadcast message to all websocket connections otherwise own connection
func (m *ConnManager) Broadcast(conn *websocket.Conn, msg *Message) {
m.Foreach(func(k, v interface{}) {
if c, ok := v.(*websocket.Conn); ok && c != conn {
if err := websocket.JSON.Send(c, msg); err != nil {
fmt.Println("Send msg error: ", err)
}
}
})
}

消息类型、格式

消息类型(MessageType)主要有单聊、群聊、系统通知等。

消息格式(MediaType)主要有文本格式、图片、文件等。

type MessageType int
type MediaType int const (
Single MessageType = iota
Group
SysNotify
OnlineNotify
OfflineNotify
) const (
Text MediaType = iota
Image
File
) // websocket message
type Message struct {
MessageType MessageType `json:"messageType"`
MediaType MediaType `json:"mediaType"`
From string `json:"from"`
To string `json:"to"`
Content string `json:"content,omitempty"`
FileId string `json:"fileId,omitempty"`
Url string `json:"url,omitempty"`
CreateAt int64 `json:"createAt,omitempty"`
UpdateAt int64 `json:"updateAt,omitempty"`
}

上面定义了一个统一的消息(Message)。

效果

前端的代码就不展示了,最终实现的聊天室效果如下:

补充

本例子没有涉及到用户认证、消息加密、idle、单聊、消息格式、消息持久化等等,只做了一个简单的群聊。

欢迎感兴趣的道友,基于此扩展出自己的推送系统、IM等。

说明

Just for fun!

Golang websocket推送的更多相关文章

  1. 用 Go 编写一个简单的 WebSocket 推送服务

    用 Go 编写一个简单的 WebSocket 推送服务 本文中代码可以在 github.com/alfred-zhong/wserver 获取. 背景 最近拿到需求要在网页上展示报警信息.以往报警信息 ...

  2. 模拟websocket推送消息服务mock工具二

    模拟websocket推送消息服务mock工具二 在上一篇博文中有提到<使用electron开发一个h5的客户端应用创建http服务模拟后端接口mock>使用electron创建一个模拟后 ...

  3. GoEasy实现websocket 推送消息通知到客户端

    最近在实现一个推送功能,用户扫描二维码签到,后台及时将签到成功信息推送到浏览器端.排除了前端ajax轮询的方式,决定采用websocket及时推送. 于是发现了第三方websocket推送库GoEas ...

  4. 小谢第37问:关于websocket推送进度,本地保存进度条,然后跳出页面进入后再显示的问题

    1.主要技术点:sessionStorage 会话存储进度 这里在使用之前,顺便说一下cookie.sessionStorage.localStorage 共同点:都是保存在浏览器端,且同源的. 区别 ...

  5. 利用奇偶数来获取websocket推送时间间隔(或者比较前一个数和下一个数的变化)

    利用奇偶数来获取websocket推送时间间隔(或者比较前一个数和下一个数的变化) 在vue中的 data () {     return { countTime: 0,         newDat ...

  6. 基于Java的WebSocket推送

    WebSocket的主动推送 关于消息推送,现在的解决方案如轮询.长连接或者短连接,当然还有其他的一些技术框架,有的是客户端直接去服务端拿数据. 其实推送推送主要讲的是一个推的概念,WebSocket ...

  7. web全栈应用【爬取(scrapy)数据 -> 通过restful接口存入数据库 -> websocket推送展示到前台】

    作为 https://github.com/fanqingsong/web_full_stack_application 子项目的一功能的核心部分,使用scrapy抓取数据,解析完的数据,使用 pyt ...

  8. 从构建分布式秒杀系统聊聊WebSocket推送通知

    秒杀架构到后期,我们采用了消息队列的形式实现抢购逻辑,那么之前抛出过这样一个问题:消息队列异步处理完每个用户请求后,如何通知给相应用户秒杀成功? 场景映射 首先,我们举一个生活中比较常见的例子:我们去 ...

  9. WebSocket推送

    本篇博客只是记录websocket在自己的项目中的应用,只是记录,不做说明(后来替换为GoEasy了). /** * 握手的设置,这其实是为了获取session */ public class Get ...

随机推荐

  1. Visual Studio - File Properties (Build Action, Copy to Output Directory)

    Ref: MSDN (https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/0c6xyb ...

  2. MySql8.0+全新身份验证方式

    我们在安装MySql8.0+的版本时MySql将会询问我们是否选择全新的身份验证方式,如下图 ⒈第一个是MySql推荐我们使用的强密码加密模式来进行身份验证 MySql8支持基于SHA256改进的更强 ...

  3. HDU-魔咒词典(字符串hash)

    魔咒词典 TimeLimit: 8000/5000 MS (Java/Others)  MemoryLimit: 32768/32768 K (Java/Others) 64-bit integer ...

  4. activemq之python使用stomp协议

    参考链接: 安装:https://pypi.org/project/stomp.py/4.1.8/#history https://www.cnblogs.com/andylhc/p/9337945. ...

  5. ebe2

    Email:kefu007@vip.qq.com 软件每天有更新.请用Ctrl+F键搜索您需要的软件..如果你找不到你需要的软件可以联系客户服务人员帮您找! 海拉之光LucidShape光学设计仿真分 ...

  6. matplotlib 中的柱状图

    def drawBar(): pyplot.bar(range(5),[100,200,300,400,400]) pyplot.xticks(range(5),['A','B','C','D','E ...

  7. pads layout 自动打地孔

    对于PCBLayout来说,后期处理也是一项费时操作,比如为了让板子耦合的更好,会在板子空旷位置打上很多地过孔.“自动打地孔”则会让你省时又省心,一不小心就提前了工期哦,哈哈! 一.下面打开一个简单的 ...

  8. docker bulid命令

    使用当前目录的 Dockerfile 创建镜像,标签为 runoob/ubuntu:v1. docker build -t runoob/ubuntu:v1 . 使用URL github.com/cr ...

  9. Centos7创建支持ssh服务的docker镜像

    如何在centos7中使用docker创建一个支持ssh连接的容器 1.拉取centos7.4镜像(由于7.4目前是最稳定的版本,所以推荐使用centos7.4) docker pull centos ...

  10. MySQL慢查询日志汇总

    概念: MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中.lo ...