后端代码:
github.com/kone-net/go…
前端代码:
github.com/kone-net/go…
go-chat
使用Go基于WebSocket的通讯聊天软件。
功能列表:
- 登录注册
- 修改头像
- 群聊天
- 群好友列表
- 单人聊天
- 添加好友
- 添加群组
- 文本消息
- 剪切板图片
- 图片消息
- 文件发送
- 语音消息
- 视频消息
- 屏幕共享(基于图片)
- 视频通话(基于WebRTC的p2p视频通话)
后端
代码仓库
go中协程是非常轻量级的。在每个client接入的时候,为每一个client开启一个协程,能够在单机实现更大的并发。同时go的channel,可以非常完美的解耦client接入和消息的转发等操作。
通过go-chat,可以掌握channel的和Select的配合使用,ORM框架的使用,web框架Gin的使用,配置管理,日志操作,还包括proto buffer协议的使用,等一些列项目中常用的技术。
后端技术和框架
- web框架Gin
- 长连接WebSocket
- 日志框架Uber的zap
- 配置管理viper
- ORM框架gorm
- 通讯协议Google的proto buffer
- makefile 的编写
- 数据库MySQL
- 图片文件二进制操作
前端
基于react,UI和基本组件是使用ant design。可以很方便搭建前端界面。
界面选择单页框架可以更加方便写聊天界面,比如像消息提醒,可以在一个界面接受到消息进行提醒,不会因为换页面或者查看其他内容影响消息接受。
前端代码仓库:
github.com/kone-net/go…
前端技术和框架
- React
- Redux状态管理
- AntDesign
- proto buffer的使用
- WebSocket
- 剪切板的文件读取和操作
- 聊天框发送文字显示底部
- FileReader对文件操作
- ArrayBuffer,Blob,Uint8Array之间的转换
- 获取摄像头视频(mediaDevices)
- 获取麦克风音频(Recorder)
- 获取屏幕共享(mediaDevices)
- WebRTC的p2p视频通话
截图
- 语音,文字,图片,视频消息
- 视频通话
- 屏幕共享
消息协议
protocol buffer协议
1 | go复制代码syntax = "proto3"; |
选择协议原因
通过消息体能看出,消息大部分都是字符串或者整型类型。通过json就可以进行传输。那为什么要选择google的protocol buffer进行传输呢?
- 一方面传输快
是因为protobuf序列化后的大小是json的10分之一,是xml格式的20分之一,但是性能却是它们的5~100倍. - 另一方面支持二进制
当我们看到消息体最后一个字段,是定义的bytes,二进制类型。
我们在传输图片,文件,视频等内容的时候,可以将文件直接通过socket消息进行传输。
当然我们也可以将文件先通过http接口上传后,然后返回路径,再通过socket消息进行传输。但是这样只能实现固定大小文件的传输,如果我们是语音电话,或者视频电话的时候,就不能传输流。
快速运行
运行go程序
go环境的基本配置
…
拉取后端代码
1 | shell复制代码git clone https://github.com/kone-net/go-chat |
进入目录
1 | shell复制代码cd go-chat |
拉取程序所需依赖
1 | shell复制代码go mod download |
MySQL创建数据库
1 | mysql复制代码CREATE DATABASE chat; |
修改数据库配置文件
1 | shell复制代码vim config.toml |
创建表
1 | shell复制代码将chat.sql里面的sql语句复制到控制台创建对应的表。 |
在user表里面添加初始化用户
1 | shell复制代码手动添加用户。 |
运行程序
1 | shell复制代码go run cmd/main.go |
运行前端代码
配置React基本环境,比如nodejs
…
拉取代码
1 | shell复制代码git clone https://github.com/kone-net/go-chat-web |
安装前端基本依赖
1 | shell复制代码npm install |
如果后端地址或者端口号需要修改
1 | shell复制代码修改src/common/param/Params.jsx里面的IP_PORT |
运行前端代码默认启动端口是3000
1 | shell复制代码npm start |
访问前端入口
1 | arduino复制代码http://127.0.0.1:3000/login |
代码结构
1 | arduino复制代码├── Makefile 代码编译,打包,结构化等操作 |
Makefile
程序打包
在根目录下执行make命令
mac
1 | bash复制代码make build-darwin |
linux
1 | bash复制代码make build |
后端proto文件生成
如果修改了message.proto,就需要重新编译生成对应的go文件。
在根目录下执行
1 | bash复制代码make proto |
如果本地没有安装proto文件,需要先进行安装,不然找不到protoc命令。
使用gogoprotobuf
安装protobuf库文件
1 | bash复制代码go get github.com/golang/protobuf/proto |
安装protoc-gen-gogo
1 | bash复制代码go get github.com/gogo/protobuf/protoc-gen-gogo |
安装gogoprotobuf库文件
1 | bash复制代码go get github.com/gogo/protobuf/proto |
在根目录测试:
1 | bash复制代码protoc --gogo_out=. protocol/*.proto |
前端proto文件生成
前端需要安装protoc buffer库
1 | bash复制代码npm install protobufjs |
生成protoc的js文件到目录
1 | bash复制代码npx pbjs -t json-module -w commonjs -o src/chat/proto/proto.js src/chat/proto/*.proto |
代码说明
WebSocket
该文件是gin的路由映射,将普通的get请求,Upgrader为socket连接
1 | go复制代码// router/router.go |
这部分对请求进行升级为WebSocket。
- c.Query(“user”)用户登录后,会获取用户的uuid,在连接到socket时会携带用户的uuid。
- 通过该uuid和connection进行关联。
- server.MyServer.Register <- client将每个client实例,通过channel进行传达,Server实例的Select会对该实例进行保存。
- client.Read(),client.Write()通过协程让每个client对自己独有的channel进行消息的读取和发送
1 | go复制代码// router/socket.go |
这是Server的三个channel,
- 用户登录后,将用户和connection绑定存放在map中
- 用户离线后,将用户从map中剔除
- 所有消息,每个client将消息获取后放入该channel中,统一在这里进行消息的分发
- 分发消息:
- 如果是单聊,直接根据前端发送的uuid找到对应的client进行发送。
- 如果是群聊,需要在数据库查询该群所有的成员,在根据uuid找到对应的client进行发送。
- 如果消息为普通文本消息,可以直接转发到对应的客户端。
- 如果消息为视频文件,普通文件,照片之类的,需要先将文件进行保存,然后返回文件名称,前端根据名称调用接口获取文件。
1 | go复制代码// server/server.go |
剪切板图片上传
上传剪切板的文件,首先我们需要获取剪切板文件。
如以下代码:
- 通过在聊天输入框,绑定粘贴命令,获取粘贴板的内容。
- 我们只获取文件信息,其他文字信息过滤掉。
- 先获取文件的blob格式。
- 通过FileReader,将blob转换为ArrayBuffer格式。
- 将ArrayBuffer内容转换为Uint8Array二进制,放在消息体。
- 通过protobuf将消息转换成对应协议。
- 通过socket进行传输。
- 最后,将本地的图片追加到聊天框里面。
1 | javascript复制代码bindParse = () => { |
上传录制的视频
上传语音同原理
- 获取视频调用权限。
- 通过mediaDevices获取视频流,或者音频流,或者屏幕分享的视频流。
- this.recorder.start(1000)设定每秒返回一段流。
- 通过MediaRecorder将流转换为二进制,存入dataChunks数组中。
- 松开按钮后,将dataChunks中的数据合成一段二进制。
- 通过FileReader,将blob转换为ArrayBuffer格式。
- 将ArrayBuffer内容转换为Uint8Array二进制,放在消息体。
- 通过protobuf将消息转换成对应协议。
- 通过socket进行传输。
- 最后,将本地的视频,音频追加到聊天框里面。
特别注意: 获取视频,音频,屏幕分享调用权限,必须是https协议或者是localhost,127.0.0.1 本地IP地址,所有本地测试可以开启几个浏览器,或者分别用这两个本地IP进行2tab测试
1 | javascript复制代码/** |
本文转载自: 掘金