分析的是hybridconnector,使用的chatofpomelo-websocket(pomelo为0.7.0)
参考:https://github.com/NetEase/pomelo/wiki/Pomelo-通讯协议
http://cnodejs.org/topic/51395fd0df9e9fcc5882576c
客户端握手:
pomelo在init时候,创建WebSocket连接,并定义onopen onmessage回调,
连接建立后,onopen被回调,客户端首先发送握手包var obj = Package.encode(Package.TYPE_HANDSHAKE, Protocol.strencode(JSON.stringify(handshakeBuffer)));
参数为,如果version在客户端有缓存,将使用缓存{
'sys': {
'version': '1.1.1',
'type': 'js-websocket'
},
'user': {
// any customized request data
}
}
这个方法中,package格式为type,length,body,传入的参数为type和body,
服务端发回message,在onmessage中处理服务器到客户端的握手响应,服务端发回的参数
{
'code': 200, // result code
'sys': {
'heartbeat': 3, // heartbeat interval in second
'dict': {}, // route dictionary
'protos': {} // protobuf definition data
},
'user': {
// any customized response data
}
}
并分发到handshake处理,首先缓存dict和protos,然后发回var obj = Package.encode(Package.TYPE_HANDSHAKE_ACK);握手ack,至此握手完毕
客户端打包:
Package分为header和body两部分。Header描述package包的类型和包的长度,body则是需要传输的数据内容。具体格式参考https://github.com/NetEase/pomelo/wiki/Pomelo-通讯协议
package的body部分发送的是Message,消息头分为三部分,flag,message id,route。具体格式参考https://github.com/NetEase/pomelo/wiki/Pomelo-通讯协议
Package.encode,打包函数比较简单,只是转换大小端
Message.encode。Message的打包函数,第一步是计算包长度,message id可变,要计算,是否有路由,要计算,加上发送的内容,就是总长度,然后创建buffer填充内容。
填充内容时候,如果有路由压缩,则在填充路由的时候,填充替换过的路由。
pomelo.encode 用户数据打包函数。这个打包的结果是Message.encode中的msg,这个函数用了处理路由压缩和protobuf压缩。
如果clientProtos中配置了路由,将用 protobuf.encode(route, msg);打包内容。如果dict定义了路由,将替换路由
程序执行的顺序刚好与以上介绍反过来,先内层后外层
客户端解包:
Package.decode,解包出类型和长度
Message.decode,解包id,路由,和body,注意id和路由可能不存在
pomelo.decode,如果有id,则说明是request,直接从routeMap中替换路由,然后deCompose,路由有compressRoute,则替换路由。serverProtos中要求打包路由,则用protobuf.decode解包内容
解包刚好是正常的执行顺序,先外层后内层
服务端:
使用的hybridconnector中的websocket链接
hybridconnector和hybridsocket是pomelo封装的服务器和socket,switch是转发。wsprocessor是websocket处理的文件,commands文件夹是握手,心跳处理的逻辑
hybridconnector用 this.tcpServer = net.createServer();接受连接。switch分发tcp和websocket。到processor处理
对于websocket的连接建立过程:
1:switch中的tcpServer转发connection到newSocket处理,newSocket将connection转发给wsprocessor处理
2:Processor.prototype.add中,httpServer分发connection消息(因为pomelo自己用net接受的连接,所以要自己用httpserver解析http协议),
http.js中用connectionListener函数处理connection消息,只是为socket添加了一下处理函数(ondata下一步会用到),
3:因为socket.ondata存在,所以直接调用,在http.js中看到ondata对应websocket来说,是为了分发upgrade消息
4:在httpserver的外层还有WebSocketServer,WebSocketServer在创建的时候,会注册httpserver的upgrade消息,用于建立websocket连接,同时分发connection消息
5:processor在创建的时候,注册了websocketserver的connection消息,并再次分发
6:switch转发了processor的connection消息
7:hybridconnector注册了switch的connection消息,用socket封装成hybridsocket,并为hybridsocket注册handshake heartbeat disconnect closing消息,同时分发connection消息
8:组件connector在初始化时,注册了hybridconnector的connection消息,用bindEvents,为hybridsocket注册了disconnect error message消息,至此连接建立
对于websocket的握手过程:
1:消息的起源处是,socket分发message消息,在hybridsocket中,收到消息,用Package.decode(msg)解包,解包函数与客户端一致,握手请求分发到handleHandshake,分发传入的socket为hybridsocket类型
2:handleHandshake将上一步解析出的msg,将body通过JSON.parse(protocol.strdecode(msg.body))转成json,用handshake消息分发出去
3:在hybridconnector中,参考【websocket的连接建立过程】7,连接时候已经注册了handshake处理方法,将转发给handshake.handle
4:在commands的handshake中,参数heartbeat dict sys被收集,通过服务端的握手包返回给客户端
5:但服务端收到握手ack包时,服务端回复客户端心跳包,(socket.emit('heartbeat');),至此握手完成
服务端心跳逻辑:
1:握手完成后,hybridsocket分发heartbeat消息,在commands中的heartbeat处理,在heartbeats字典中,存储计时器,将在心跳时间后,发送心跳包,并注册超时函数
2:当收到客户端的心跳包后,在hybridsocket处handleHeartbeat中处理,分发heartbeat消息。在commands中的heartbeat中,要先清理超时回调,然后同1
服务端响应request:
1:消息的起源处是,socket分发message消息,在hybridsocket中,收到message消息,用Package.decode(msg)解包,解包函数与客户端一致,通过hybridsocket分发解析后的msg
2:参考【websocket的连接建立过程】8,在connector处理了解析后的msg,首先调用 self.connector.decode(msg);hybridconnector中的decode与客户端的解包一致,
先调用Message.decode(msg.body);然后替换路由,和用protobuf解压缩
3:然后handleMessage,将解析后的请求,分发到对应的服务器处理,回调的结果通过self.send(msg.id, msg.route, resp, [session.id], {isResponse: true}, function() {});返回给客户端
send中会将发送的内容通过emsg = this.connector.encode(reqId, route, msg);打包成message,代码在hybridconnector中,与客户端逻辑相同
4:connector的send方法,通过组件__pushScheduler__发送,并在下一帧调用回调函数(reqest的回调为空函数)
5:组件__pushScheduler__发送,不论是广播还是推送,都是通过sessionService.sendMessage发送,然后调用session的send方法
6:sessionservice逻辑中,session是在【websocket的连接建立过程】8中的bindevents中生成的,成员变量__socket__是hybridsocket,所以,session的send方法通过hybridsocket的send实现
7:hybridsocket的send中this.sendRaw(Package.encode(Package.TYPE_DATA, msg)); 至此服务端完成对request的响应
服务端的push不再详说