分析的是hybridconnector,使用的chatofpomelo-websocket(pomelo為0.7.0)
參考:https://github.com/NetEase/pomelo/wiki/Pomelo-通訊協議
http://cnodejs.org/topic/51395fd0df9e9fcc5882576c
client握手:
pomelo在init時候,建立WebSocket連線。並定義onopen onmessage回撥,
連線建立後,onopen被回撥。client首先發送握手包var obj = Package.encode(Package.TYPE_HANDSHAKE, Protocol.strencode(JSON.stringify(handshakeBuffer)));
引數為。假設version在client有快取,將使用快取{
'sys': {
'version': '1.1.1',
'type': 'js-websocket'
},
'user': {
// any customized request data
}
}
這種方法中,package格式為type。length。body。傳入的引數為type和body,
服務端發回message,在onmessage中處理server到client的握手響應,服務端發回的引數
{
'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,至此握手完成
client打包:
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定義了路由,將替換路由
程式執行的順序剛好與以上介紹反過來,先內層後外層
client解包:
Package.decode,解包出型別和長度
Message.decode。解包id,路由。和body,注意id和路由可能不存在
pomelo.decode。假設有id。則說明是request,直接從routeMap中替換路由,然後deCompose,路由有compressRoute,則替換路由。serverProtos中要求打包路由,則用protobuf.decode解包內容
解包剛好是正常的執行順序,先外層後內層
服務端:
使用的hybridconnector中的websocket連結
hybridconnector和hybridsocket是pomelo封裝的server和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)解包,解包函式與client一致,握手請求分發到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被收集,通過服務端的握手包返回給client
5:但服務端收到握手ack包時。服務端回覆client心跳包,(socket.emit('heartbeat');)。至此握手完畢
服務端心跳邏輯:
1:握手完畢後,hybridsocket分發heartbeat訊息,在commands中的heartbeat處理,在heartbeats字典中。儲存計時器,將在心跳時間後,傳送心跳包。並註冊超時函式
2:當收到client的心跳包後。在hybridsocket處handleHeartbeat中處理。分發heartbeat訊息。
在commands中的heartbeat中。要先清理超時回撥,然後同1
服務端響應request:
1:訊息的起源處是,socket分發message訊息。在hybridsocket中,收到message訊息,用Package.decode(msg)解包,解包函式與client一致,通過hybridsocket分發解析後的msg
2:參考【websocket的連線建立過程】8,在connector處理了解析後的msg,首先呼叫 self.connector.decode(msg);hybridconnector中的decode與client的解包一致,
先呼叫Message.decode(msg.body);然後替換路由。和用protobuf解壓縮
3:然後handleMessage。將解析後的請求,分發到相應的server處理,回撥的結果通過self.send(msg.id, msg.route, resp, [session.id], {isResponse: true}, function() {});返回給client
send中會將傳送的內容通過emsg = this.connector.encode(reqId, route, msg);打包成message,程式碼在hybridconnector中,與client邏輯同樣
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不再詳說