最近工作中需要用到基于comet技术的服务端推技术实现一个类似与聊天的东东,研究了下cometd框架。写了一个简单的demo,基于此demo,跟踪了下源码,了解一些客户端与服务端的交互流程。
服务端如何实现和初始化就不说了,想重点说的是服务端初始化后,客户端发起连接后的交互流程:
1. 首先客户端和服务端交互采用的Bayeux协议。
2. 客户端向服务端 (/meta/handshake频道上) 发起握手请求。Bayeux协议要求,一个新的客户端发送的第一条消 息是一个握手信息。
3. 服务端收到握手请求,BayeuxServer创造了一个半服务器对象的实例(一个ServerSession)。服务端处理完握手请求后,发送响应给客户端。
4. 客户端收到服务端处理握手请求的响应,如果它是成功,接下来要做两件事情:
a.向服务端发起订阅服务的请求。
b.开始与服务端的心跳机制,来交换连接的信息。
5. 以实现的demo为例,服务端再启动时初始化了两个服务频道
(1./service/privatechat 2./service/joinlisten )这两个频道均为自定义频道(其实就是两个http接口),需要写代码实现业务逻辑。
6. 客户端向服务端发起订阅 /service/privatechat 服务,此服务用来实现用户之间的聊天。
如clientA订阅了此服务,当clientB向clientA发送聊天信息时,服务端会立即采用comet推送机制,将聊天信息推送给clientA
7. 开始心跳机制。
8. 心跳机制(在使用http传输时,cometd使用的是http传输),也被称为“长轮询”。
9. 心跳机制,允许客户端检测服务器是否关闭了,并允许服务端检测客户端是否关闭了。
10. 客户端和服务器之间的连接的消息一直都有,直至任何一方决定中断并发送一个
disconnect 的消息(发送/meta/disconnect 断开通道消息)
心跳机制先讲到这里,后续详细再说。
11. 此时客户端可以向服务端/service/joinlisten 频道发送请求,向服务端加入聊天,此接口为服务端自定义的接口,
客户端需要调用此接口,加入聊天,服务端记录下客户端信息,类似于登录。
12. 此时所有初始化工作都已准备完毕,客户端之间就可以任意发送聊天信息了。
13.comed的设计有一处巧妙之处:
大家都知道http是请求响应模式,也就是一次请求对应一次响应,服务端推送信息,是没有客户端请求的,
这样就破坏了http的交互模式,comed的实现机制是,客户端向服务端发起心跳请求,服务端会挂起这个请求,先不回响
应,当达到心跳时间30秒再回响应,客户端收到响应立即再次发起心跳请求,也就是说服务端会一直挂起一个没有处理的心跳请求,当服务端有信息需要推送给客户端时,会把之前挂起的心跳请求的响应和推送的信息作为一个响应消息包,推送给客户端(也可以理解为把之前挂起的响应回给客户端),这样就没有说是无端端的多了推送这种机制,更好的遵循了http请求,响应的规范。
××××××××××××××××××××××××××××××
以下是跟踪demo的交互消息包:
1.客户端向 /meta/handshake 发送握手请求信息。
采用post请求[{"id":"1","supportedConnectionTypes":["long-polling"],"channel":"/meta/handshake","version":"1.0"}]
2.服务端收到请求后 返回响应:
[{"id":"1","minimumVersion":"1.0","supportedConnectionTypes":["callback-polling","long-polling"],"successful":true,"channel":"/meta/handshake","clientId":"21541woc90dqi918d826hcb0bpy","version":"1.0"}]
客户端根据successful 判断是否握手成功,
3.客户端向 /service/privatechat 发送订阅聊天监听请求。
[{"id":"2","subscription":"/service/privatechat","channel":"/meta/subscribe","clientId":"21541woc90dqi918d826hcb0bpy"}]
4.服务端收到请求后 返回响应:
[{"id":"2","subscription":"/service/privatechat","successful":true,"channel":"/meta/subscribe"}]
客户端根据successful 判断是否订阅成功,
5.客户端向 /meta/connect 发送第一次心跳请求
[{"id":"3","connectionType":"long-polling","advice":{"timeout":0},"channel":"/meta/connect","clientId":"21541woc90dqi918d826hcb0bpy"}]
6.服务端收到请求后返回相应:
[{"id":"3","successful":true,"advice":{"interval":0,"reconnect":"retry","timeout":30000},"channel":"/meta/connect"}]
7.后续的心跳请求响应与第一次稍稍有区别:
[{"id":"5","connectionType":"long-polling","channel":"/meta/connect","clientId":"21541woc90dqi918d826hcb0bpy"}]
[{"id":"5","successful":true,"channel":"/meta/connect"}]
8.向/service/privatechat 发送聊天信息请求:
[{"id":"38","data":{"peer":"gaolp","chat":"hello world!","user":"gaolp"},"channel":"/service/privatechat","clientId":"21541woc90dqi918d826hcb0bpy"}]
[{"id":"38","data":{"scope":"private","chat":"hello world!","user":"gaolp"},"channel":"/service/privatechat"},{"id":"38","successful":true,"channel":"/service/privatechat"}]