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. P1052 过河

    动态规划的好题 状态转移很简单,dp[i] = dp[i-k] + st[i] ,k是移动距离,st[i]判断i位置是否有石头,但是距离太大,需要压缩路径. K∈[1,10],lcm[1,10] = ...

  2. IDEA中执行MAVEN命令打jar包

    SpringBoot Jar包打包 1.工程POM配置packaging为jar. <packaging>jar</packaging> 2.增加MAVEN运行配置 添加MAV ...

  3. ubuntu server 16.04 安装过程中提示无法安装busybox-initramfs

    这个问题在安装desktop版本时是不会出现的,只有server才有这个问题. 出现这个问题与硬件平台无关,不管是虚拟机还是物理机都会出现,解决的办法是在安装开始界面选择English,后面Langu ...

  4. 01pxc集群的部署

    尽可能的控制pxc集群的规模,pxc集群节点越多,数据同步的速度就越慢 所有pxc节点的硬件配置最好相同,pxc集群数据同步的速度取决于配置最低的节点 Pxc集群只支持innodb引擎 安装pxc集群 ...

  5. idea的一些设置

    在File->Settings->Appearance & Behavior->System Settings->Updates下取消Automatically che ...

  6. spring security5.0源码导入idea

    资源路径:链接:https://pan.baidu.com/s/1Xep6hzyIF2a0AtFYDeO_bw提取码:6af8 1.解压源码 2.编译源码     windows下:直接双击gradl ...

  7. 详解MariaDB数据库的索引

    1.什么是索引 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针. 更通俗的说,数据库索引好比是一本书前面的目录,在查找内容之前可以先在 ...

  8. js拼音排序

    js拼音排序 var arr =['a','c','b','b']; arr.sort( function compareFunction(p1, p2) { return p1.localeComp ...

  9. .Net Core小技巧 - Hosted Services + Quartz实现定时任务调度

    背景 之前一直有朋友问,.Net Core + Linux环境有没有类似Windows服务的东西.其实是有的,我了解的方法有两种: #1 创建一个ASP.Net Core的Web项目(如Web API ...

  10. MongoDB与SpringBoot整合(支持事务)

    1.创建SpringBoot工程,选择 Web.MonogDB 依赖,pom如下: <parent> <groupId>org.springframework.boot< ...