NIO应用之Comet4j
一、简介
Comet技术是服务器推技术的一个总称,Comet被称为"基于HTTP长连接的服务器推技术",其具体实现方式是长轮询和流。 这两种实现方式都有一个很大的问题。请求需要在服务器上存在一段较长的时间。这使得每一个请求连接的线程一直没有释放,且一直处于空闲状态。
阻塞IO: 当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。
Java NIO的非阻塞模式:使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。
二、项目实战
2.1 准备工作
下载服务端jar文件:
Comet4J目前仅支持Tomcat6、7版本,根据您所使用的Tomcat版本下载【comet4jtomcat6.jar】或【comet4j-tomcat7.jar】
下载客户端js文件:
下载【comet4j.js】到您的项目中。
修改服务器配置文件:
因为Comet4J工作在NIO方式下,所以我们需要调整服务器连接器配置,更换为NIO连接器。 打开server.xml文件将找到原先的连接器配置:
<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8080"
protocol="org.apache.coyote.http11.Http11NioProtocol"
redirectPort="8443"/>
2.2 客户端
客户端是一个JavaScript文件(comet4j-0.0.2.js),其中最重要的是JS.Connector和JS.Engine两个类。
JS.Connector负责与服务器建立并保持连接,而JS.Engine类负责将服务器推送过来的消息转化为开发人员可以处理的消息事件,并分发出去,大多数情况下,我们仅需要使用JS.Engine类就可以完成多数的开发工作。
JS.Engine类是一个静态类,在一个页面中只有一个JS.Engine类的实例。它除了负责把服务器推过来的消息转化为事件分发以外,与服务器的连接与断开也由此类负责。
JS.Engine.start方法
JS.Engine.start(String str)和JS.Engine.stop(String str)分别控制连接和断开动作,start方法需要传入一个字符串参数,用来指定您配置的Comet4J连接地址。比如按前面准备工作的配置了CometServlet的地址为/conn,那么可以这样写:
JS.Engine.start('/conn');
上段代码我们让浏览器与服务器进行连接,当连接成功以后JS.Engine类会发出"start"事件,如何进行事件的处理我们稍后介绍。
我们也能够让连接断开:
JS.Engine.stop('主动断开');
上面代码我们让连接断开,并传入了一个“主动断开”这样一个断开的原因。如果您并不需要对断开的原因进行说明,也可以不传递参数:
JS.Engine.stop();
JS.Engine类的事件处理
上面我们介绍了如何使用start和stop方法来建立和断开连接,当成功建立连接已后JS.Engine会发出"start"事件,当断开后会发出“stop”事件,当收到某个通道推送过来的信息时也会发出与通道标识同名的事件。您可以事先在中使用JS.Engine.on方法来注册事件处理函数。例如:
JS.Engine.on({
start : function(cId, channelList, engine){
alert('连接已建立,连接ID为:' + cId);
},
stop : function(cause, cId, url, engine){
alert('连接已断开,连接ID为:' + cId + ',断开原因:' + cause + ',断开的连接地址:'+ url);
}
});
2.3 服务器端
服务端是CometContext和CometEngine两个重要的类。
CometContext 是一个单态类,通过getInstance方法来获取实力,它主要负责框架的一些初始化工作保存着一些参数的配置值,负责注册应用通道标识。如戏注册一个通道表示:
CometContext.getInstance().registChannel("channelDemo");
这样便注册了一个标识为 channelDemo 的应用通道,二客户也可以通过JS.Engine.on('hello',function(msg){...})的形式来接收并处理来自次通道的信息。
CometEngine类
除了负责对连接的处理之外,对于开发人员而言,更加常用的可能是它所提供的sendTo或者sendToAll方法对客户端发送消息:
Sring channel = "channelDemo"; String someConnectionId = "1125-6634-888"; engine.sendToAll(channel , "我来了!"); //发送到前段js engine.sendTo(channel , engine.getConnection(someConnectionId),“Hi,我是XXX”);
CometEngine也是框架工作的事件引擎的集散地,它提供了BeforeConnectEvent、BeforeDropEvent、ConnectEvent、DropEvent、MessageEvent等事件。通过对这些事件的处理来实现具体的功能:
class JoinListener extends ConnectListener { @Override public boolean handleEvent(ConnectEvent anEvent) { CometConnection conn = anEvent.getConn(); CometContext.getInstance().getEngine().sendTo("hello", conn.getId(),"欢迎上线"); } } CometEngine engine = CometContext.getInstance().getEngine(); engine.addConnectListener(new JoinListener()