go-chat

使用Go基于WebSocket的通讯聊天程序。

git仓库:

后端代码

前端代码

功能列表:

  • 登录注册
  • 修改头像
  • 群聊天
  • 群好友列表
  • 单人聊天
  • 添加好友
  • 添加群组
  • 文本消息
  • 剪切板图片
  • 图片消息
  • 文件发送
  • 语音消息
  • 视频消息
  • 屏幕共享(基于图片)
  • 视频通话(基于WebRTC的p2p视频通话)
  • 分布式部署(通过kafka全局消息队列,统一消息传递,可以水平扩展系统)


后端

代码仓库
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。可以很方便搭建前端界面。

界面选择单页框架可以更加方便写聊天界面,比如像消息提醒,可以在一个界面接受到消息进行提醒,不会因为换页面或者查看其他内容影响消息接受。
前端代码仓库:
https://github.com/kone-net/go-chat-web


前端技术和框架

  • React
  • Redux状态管理
  • AntDesign
  • proto buffer的使用
  • WebSocket
  • 剪切板的文件读取和操作
  • 聊天框发送文字显示底部
  • FileReader对文件操作
  • ArrayBuffer,Blob,Uint8Array之间的转换
  • 获取摄像头视频(mediaDevices)
  • 获取麦克风音频(Recorder)
  • 获取屏幕共享(mediaDevices)
  • WebRTC的p2p视频通话


截图

  • 语音,文字,图片,视频消息


  • 视频通话
  • 屏幕共享


消息协议


protocol buffer协议



选择协议原因

通过消息体能看出,消息大部分都是字符串或者整型类型。通过json就可以进行传输。那为什么要选择google的protocol buffer进行传输呢?

  • 一方面传输快
    是因为protobuf序列化后的大小是json的10分之一,是xml格式的20分之一,但是性能却是它们的5~100倍.
  • 另一方面支持二进制
    当我们看到消息体最后一个字段,是定义的bytes,二进制类型。
    我们在传输图片,文件,视频等内容的时候,可以将文件直接通过socket消息进行传输。
    当然我们也可以将文件先通过http接口上传后,然后返回路径,再通过socket消息进行传输。但是这样只能实现固定大小文件的传输,如果我们是语音电话,或者视频电话的时候,就不能传输流。


快速运行


运行go程序

go环境的基本配置
...

拉取后端代码


进入目录


拉取程序所需依赖


MySQL创建数据库


修改数据库配置文件


创建表


在user表里面添加初始化用户


运行程序



运行前端代码

配置React基本环境,比如nodejs
...

拉取代码


安装前端基本依赖


如果后端地址或者端口号需要修改


运行前端代码默认启动端口是3000


访问前端入口



代码结构



Makefile


程序打包

在根目录下执行make命令
mac


linux



后端proto文件生成

如果修改了message.proto,就需要重新编译生成对应的go文件。
在根目录下执行


如果本地没有安装proto文件,需要先进行安装,不然找不到protoc命令。
使用gogoprotobuf

安装protobuf库文件


安装protoc-gen-gogo


安装gogoprotobuf库文件


在根目录测试:



前端proto文件生成

前端需要安装protoc buffer库


生成protoc的js文件到目录



代码说明


WebSocket

该文件是gin的路由映射,将普通的get请求,Upgrader为socket连接


这部分对请求进行升级为WebSocket。

  • c.Query("user")用户登录后,会获取用户的uuid,在连接到socket时会携带用户的uuid。
  • 通过该uuid和connection进行关联。
  • server.MyServer.Register <- client将每个client实例,通过channel进行传达,Server实例的Select会对该实例进行保存。
  • client.Read(),client.Write()通过协程让每个client对自己独有的channel进行消息的读取和发送


这是Server的三个channel,

  • 用户登录后,将用户和connection绑定存放在map中
  • 用户离线后,将用户从map中剔除
  • 所有消息,每个client将消息获取后放入该channel中,统一在这里进行消息的分发
  • 分发消息:
    • 如果是单聊,直接根据前端发送的uuid找到对应的client进行发送。
    • 如果是群聊,需要在数据库查询该群所有的成员,在根据uuid找到对应的client进行发送。
    • 如果消息为普通文本消息,可以直接转发到对应的客户端。
    • 如果消息为视频文件,普通文件,照片之类的,需要先将文件进行保存,然后返回文件名称,前端根据名称调用接口获取文件。




剪切板图片上传

上传剪切板的文件,首先我们需要获取剪切板文件。
如以下代码:

  • 通过在聊天输入框,绑定粘贴命令,获取粘贴板的内容。
  • 我们只获取文件信息,其他文字信息过滤掉。
  • 先获取文件的blob格式。
  • 通过FileReader,将blob转换为ArrayBuffer格式。
  • 将ArrayBuffer内容转换为Uint8Array二进制,放在消息体。
  • 通过protobuf将消息转换成对应协议。
  • 通过socket进行传输。
  • 最后,将本地的图片追加到聊天框里面。



上传录制的视频

上传语音同原理

  • 获取视频调用权限。
  • 通过mediaDevices获取视频流,或者音频流,或者屏幕分享的视频流。
  • this.recorder.start(1000)设定每秒返回一段流。
  • 通过MediaRecorder将流转换为二进制,存入dataChunks数组中。
  • 松开按钮后,将dataChunks中的数据合成一段二进制。
  • 通过FileReader,将blob转换为ArrayBuffer格式。
  • 将ArrayBuffer内容转换为Uint8Array二进制,放在消息体。
  • 通过protobuf将消息转换成对应协议。
  • 通过socket进行传输。
  • 最后,将本地的视频,音频追加到聊天框里面。

特别注意: 获取视频,音频,屏幕分享调用权限,必须是https协议或者是localhost,127.0.0.1 本地IP地址,所有本地测试可以开启几个浏览器,或者分别用这两个本地IP进行2tab测试