[微信协议分析] 多点登陆
声明:微信客户端协议是二进制协议而且加密,难以分析协议具体编码格式,我不做逆向工程。只是简单抓包分析业务的实现流程,在这里记录下来用于参考学习,并不是破解协议。
IM产品的多点登陆逻辑特别复杂,很难做到很好的用户体验,就像新版mac handoff 功能也不少人在喷。
微信最开始并不支持多点登陆,后来陆续增加的Web版、Mac版,但并不是完整意义的客户端,要说只是辅助输入。
微信允许: 一个移动端(下面称之为主客户端) + 一个web/mac 同时在线(下面称之为从客户端),web/mac 只能接收在线消息、发消息,不记录消息历史。这样多点逻辑就变得相对简单很多了。
存储
服务器不用保存完整消息历史,通过客户端对push消息的ack保证消息送达,协议保证消息至少一次推送到主客户端,然后消息即可删除;服务器只存储未下发到主客户端的消息。
多点时:主客户端依然采用Push推送消息(只是应该会保留一小段时间消息记录等待从Sync),从客户端Sync消息;如果主不在线,消息记录不会删除,等主重新连上下载离线消息。
服务器不存储消息历史,一个是安全,再者节省硬件成本,大量短消息的存储和读取成本是非常高的,因为基本都是随机IO。whatapp 用500台机器,支撑1亿在线,虽然有100w/s 消息,只离线消息存储量是很少的。
未读数同步
这个很好解决,如果客户端知道自己处于多端在线情况下时,进入会话时,需要告诉服务器消息已读。消息已读也保存为一条消息,再通过Sync协议,同步到另外的客户端。web 微信会调用webwxstatusnotify 同步未读。
未读变化也当成一条消息存储,且只在多端情况下存在,单点在线时未读数由客户端维护。
删除消息
不管是否多点在线任何删除消息操作都会同步到服务器,避免删除的消息下次有不小心同步回来了,服务器可能及时删除、也可能长期保存,客户端每次上报就没错了。
移动客户端消息删除不会同步到 web版。
自己同步自己
每个操作(发消息、清未读等),应答后,因为SyncKey 变化了,Sync协议上会产生一个空的Sync操作用于更新SyncKey。
为啥不通过应答更新SyncKey? 并发情况直接更新会造成丢消息问题,也保证协议的统一性。
对于主客户端:
- 单点在线时发消息,并不会同送空的SyncKey,SyncKey应该是通过应答防护,节省同步流量。怎么防止丢消息呢?
猜想是这样: 请求时带上SyncKey(local), 服务器在inc SyncKey得到SyncKey(current) == SyncKey(local) 时,直接返回SyncKey(current),否则返回Sync(local) 并推送New SyncKey Notify。因为此时确保客户端没有未收消息,坏情况应答比新消息慢,也不会有啥问题。
- 多点在线时,发消息和从客户端一样,也会自己同步自己