java web 服务器推送技术--comet4j

article/2025/10/3 18:13:14

1、背景

首先实现服务器推送技术一直一来是B/S应用开发的一块难题,因为是基于HTTP协议的,HTTP协议为无状态,单向性的协议即,必须由客户端发起一个请求建立连接,服务器接收请求,把数据返回给客户端,然后释放连接。下一次,再由客户端发起另一次请求,重复上述过程。服务器始终处于"被动"地位。这种情况导致只有客户端请求,服务器才能被动响应结果,虽然HTTP协议的优势是很大的,高效,高伸缩性等。

问题1:

但是有优势自然有不足,其不能满足有实时性要求的应用的需求,譬如我想做个聊天室、实时推送比赛的比分、股票行情实时显示等,这种情况导致服务器无法主动向客户端推送消息,这就有了瓶颈。


怎么样对请求做出响应并发送初始数据之后,仍然保持与用户的交互呢?

....于是人们就开始寻找各种解决方案了。

解决方案1:

客户端的页面不断的进行ajax请求,应该很好实现吧。js定时器就可以实现,每次请求如果服务器端有更新数据则响应到客户端。


问题2:

但是这会造成服务器的严重压力,如果在线用户数量过多的话,每隔个一两秒请求一次,哪个服务器能受得了,这种肯定不太现实,或者是最无奈的实现方法。

解决方案2:

于是出现了 comet ,comet技术是服务器推技术的一个总称Comet被称为"基于HTTP长连接的服务器推技术",但不是具体实现方式。下面我将会讲两种实现方式:

长轮询(long-polling)方式,它同样使用的ajax,简单说一下,就是客户端使用ajax发送一个请求,HTTP的连接保持,服务器端会阻塞请求,直到服务器端有一个事件触发或者到达超时。服务器端肯定会开启一个线程,这个线程会时时监测要请求的数据是否有变化,如果有变化,则向客户端输出最新消息,并关闭链接,客户端收到消息处理之后,再次向服务器端请求,如此循环,所以叫长轮询,这种实现方式比起上一种自然要好的多了,不需要客户端不断的ajax请求,减轻服务器端的一定压力,而且可以算得上是实时的。因为这种方案基于 AJAX,请求异步发出,无须安装插件,IE、Mozilla FireFox 都支持。


流方式(长连接),这种和长轮询方式挺像,只有一点区别,就是流方式是在客户端请求服务端并建立链接之后,服务器端始终不会关闭链接(直到超时,断电或者其他特殊情况)每次有数据时,就向客户端进行输出,而不像长轮询每次向客户端输出之后,都要关闭链接。这种方式的另一个优点是可以大大减少发送到服务器的请求,从而避免了与设置服务器连接相关的开销和延时。不幸的是,XMLHttpRequest 在不同的浏览器中有很多不同的实现。这项技术只能在较新版本的 Mozilla Firefox 中可靠地使用。对于 Internet Explorer 或 Safari,仍需使用长轮询。


在长轮询方式下,客户端是在 XMLHttpRequest 的 readystate 为 4(即数据传输结束)时调用回调函数,进行信息处理。当 readystate 为 4 时,数据传输结束,连接已经关闭。Mozilla Firefox 提供了对流方式的支持,即readystate 为 3 时(数据仍在传输中),客户端可以读取数据,从而无须关闭连接,就能读取处理服务器端返回的信息。IE 在 readystate 为 3 时,不能读取服务器返回的数据,目前 IE 不支持流方式。


问题3:

长轮询和流都有一个很大的问题。请求需要在服务器上存在一段较长的时间。这打破了每个请求使用一个线程的模型,因为用于一个请求的线程一直没有被释放。更糟糕的是,除非要发回数据,否则该线程一直处于空闲状态。这显然不具有可伸缩性。


解决方案3:

为了有效地处理 Comet,需要非阻塞 IO,Java 通过它的 NIO 库提供非阻塞 IO。两种最流行的开源服务器 Apache Tomcat 和 Jetty 都利用 NIO 增加非阻塞 IO,从而支持 Comet。

阻塞IO:当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 

Java NIO的非阻塞模式:使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 


2、Comet4j开发指南

2.1 准备工作

下载服务端jar文件:
Comet4J目前仅支持Tomcat6、7版本,根据您所使用的Tomcat版本下载【comet4jtomcat6.jar】或【comet4j-tomcat7.jar】文件放置到WEB项目的WEB-INF\lib目录下。

下载客户端js文件:
下载【comet4j.js】到您的项目中,比如:WebContent\js目录下。

修改服务器配置文件:
因为Comet4J工作在NIO方式下,所以我们需要调整服务器连接器配置,更换为NIO连接器。 打开server.xml文件将找到原先的连接器配置:

<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />  
替换为:

<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443"/>


2.2 编写客户端

在客户端,发出AJAX 。



2.3 编写服务端
编写Servlet。通过 servlet 实现 CometProcessor 接口。这个接口要求实现 event()方法,在配置的 Http11NioProtocol调用 event()方法来处理请求,而不是 doGet 或 doPost。在event() 方法中,分别处理连接开始(BEGIN)、新数据可用(READ),连接结束(END),或出错等事件。Comet 允许针对不同的事件指定不同的连接超时。这意味着可以给常规的请求设置很短的生命周期,但是对于响应长连接请求的机制,可以将这个生命周期延长至几分钟。

Servlet处理 Comet 事件:


推送随机数的线程:


      

3、Comet4J浅析

3.1 客户端

客户端是一个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.stop(); 

JS.Engine类的事件处理
上面我们介绍了如何使用start和stop方法来建立和断开连接,当成功建立连接已后JS.Engine会发出"start"事件,当断开后会发出“stop”事件,当收到某个通道推送过来的信息时也会发出与通道标识同名的事件。您可以事先在中使用JS.Engine.on方法来注册事件处理函数。例如:
 

JS.Engine.on('start',function(cId, channelList, engine){  alert('连接已建立,连接ID为:' + cId);  
});  
JS.Engine.on('stop',function(cause, cId, url, engine){  alert('连接已断开,连接ID为:' + cId + ',断开原因:' + cause + ',断开的连接地址:'+ url);  
}); <strong> </strong>
也可以将上段代码写成,下面代码与上段代码完全等效:
JS.Engine.on({  start : function(cId, channelList, engine){  alert('连接已建立,连接ID为:' + cId);  },  stop : function(cause, cId, url, engine){  alert('连接已断开,连接ID为:' + cId + ',断开原因:' + cause + ',断开的连接地址:'+ url);  }  
});
接下来,介绍一下如何对服务器推送过来的消息进行处理。在介绍之前,我们假设后台已经注册了一个"hello"的应用通道标识,并且只向客户端推送简单的字符串信息。先看如下代码:

JS.Engine.on('hello',function(text){  alert(text);  
});
这样当服务器端使用"hello"通道标识推送过来的消息就可以由上段代码进行处理,将推送过来的信息弹出。
特别注意:以上代码在事件处理函数中使用了alert仅为说明函数功能,实际使用中,在事件处理函数中切勿使用alert、prompt、confirm等可以中断脚本运行的函数,因为Engine需要实时的保持工作状态。
 


3.2 服务器端

服务端由一个Jar包组成,其中最重的是CometContext和CometEngine两个类。


Comet Context 类
CometContext是一个单态类,通过其getInstance方法来获得实例,它主要负责框架的一些初始化工作保存着一些参数的配置值,除此之外它还有一个更重要的职责——负责注册应用通道标识。如果您想使用框架来实现自己的应用,那么您必需要为自己的应用分配一个唯一的通道标识,并将此通道标识在WEB容器启动时使用CometContext的registChannel方法进行注册,这样,客户端才可以正确接受此应用所推送的消息。注册一个通道标识非常简单   

CometContext.getInstance().registChannel("hello");
这样便注册了一个标识为“hello”的应用通道,而客户也可以通过JS.Engine.on('hello',function(msg){...})的形式来接收并处理来自此通道的消息。


Comet Engine 类
另一个重要的类是CometEngine,它除了负责对连接的处理之外,对于开发人员而言,更加常用的可能是它所提供的sendTo或sendToAll方法来向客户端发送消息:  

Sring channel = "hello";  
String someConnectionId = "1125-6634-888";  
engine.sendToAll(channel , "我来了!");  
engine.sendTo(channel , engine.getConnection(someConnectionId),“Hi,我是XXX”); 
上面代码使用sendToAll方法向所有客户端在"hello"通道上发送了“我来了!”这样一条消息,然后又使用sendTo在同样的通道上向某一个连接发送了“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()  

上面先定义了一个JoinListener并实现了父类ConnectListener的handleEvent抽像方法,然后使用engine.addConnectListener来注册这个事件侦听。这样,在有客户与服务器成功建立连接已后,就可以向此客户端推送一条欢迎信息。


4、Comet4j在自己项目中的应用

主要就两个问题进行说明:

1、comet是怎样实现HTTP长连接的?

2、服务器端何时将数据发送给浏览器以及怎样发送的?


几个比较重要的类:

CometContext  上下文,负责初始化配置、引擎对象、连接器对象、消息缓存等。

CometEngine        引擎,负责管理和维持连接,并能够必要的发送服务

CometConnection   Comet连接,负责构造一个连接的实例

ConnectionDTO    连接成功后所发送的数据对象


当访问含有JS.Engine.start方法(登陆站点成功后的页面都有此方法)页面时:

1.客户端向浏览器发送ajax请求:http://125.220.159.169/WebHr/conn?cmd=conn&cv=0.0.2&ram=0.728455702541396

cmd:客户端连接器动作标识  值有:conn(第一次连接) revival(复活连接) drop(断开连接)

cv:版本号

ram:随机数

2.服务器端进行一系列的处理,会将请求交给第一次连接处理的函数CometEngine.connect()---建立用户连接

(1)将请求封装成CometConnection,在进行一些连接前的事件处理

(2)把要发送的数据封装ConnectionDTO ,并向客户端送数据。若此时连接中断,则将数据放入缓存ConcurrentHashMap<CometConnection, List<CometMessage>>(发送未成功的消息)

发送数据内容:<{"channel":"c4j","data":{"cId":"a58d8c9c-dcf8-40f6-addb-a5729a796226","channels":
["inbox"],"timeout":"60000","ws":"lpool"},"time":"1437050575307"}>

其中cid是用于标记CometConnection连接的。不同窗口cid不一样。

(3)将建立的连接加入ConcurrentHashMap<String, CometConnection> (连接池)其中String:cid

3.客户端收到回复,再次向服务器发送ajax请求http://125.220.159.169/WebHr/conn?cmd=revival&cid=a58d8c9c-dcf8-40f6-addb-a5729a796226&ram=0.5657808198593557  cid即为服务器端生成,标记同一连接的符号。

4.服务器响应函数:CometEngine.revival()连接复活

CometConnection conn = ct.getConnection(cId);根据传过来的cid获取到上次连接的CometConnection对象,将其复活。即是服务器端认为还是第一次的Http连接请求,实现了HTTP长连接(问题1解决!)。之后向这一连接上发消息,消息来源为cache,也就是ConcurrentHashMap<CometConnection, List<CometMessage>>里面未发送成功的消息。消息为空则不执行发送操作。


服务器端何时将数据发送给哪个用户以及怎样发送的?

当浏览器与服务器建立一个连接的时候,JoinListener extends ConnectListener,会触发JoinListener的处理函数,此时会将连接加入到ConcurrentHashMap<String, List<String>>第一参数用户id,第二参数对应的链接cid集合。这样向用户发消息就有了对应的链接cid。那何时触发发送消息的动作?

当有用户A向用户B发私信时,首先会将私信内容保存在数据库。保存完后会有一个处理动作,那就是通知服务器向B发私信(此时B并未刷新页面,或是B并不在线)。服务器根据B.id,查找上面的ConcurrentHashMap<String, List<String>>,若B在线,则向B发送消息,若B不在线,则不执行任何操作。




参考来源:

[原]java web 服务器推送技术 comet实现(原理)

使用 Java 实现 Comet 风格的 Web 应用

浅析Comet技术在Java Web实时系统开发中的应用

comet4j




http://chatgpt.dhexx.cn/article/NFsEbNxK.shtml

相关文章

服务器之间数据文件推送,数据库数据推送到另外服务器

数据库数据推送到另外服务器 内容精选 换一换 该方案优点是简单,容易上手,缺点是停机时间较长。因此它适用于数据量不大,或者允许停机的时间较长,并且在这个时间范围内能够完成的数据。由于云数据库RDS服务提供的服务与原来的数据库服务是完全兼容的,所以对于用户来说,从…

服务器推送技术之短轮询、长轮询、SSE和Websocket

服务器推送技术 服务器推送技术干嘛用&#xff1f;就是让用户在使用网络应用的时候&#xff0c;不需要一遍又一遍的去手动刷新就可以及时获得更新的信息。大家平时在上各种视频网站时&#xff0c;对视频节目进行欢乐的吐槽和评论&#xff0c;会看到各种弹幕&#xff0c;当然&a…

【JavaWeb】小白也能看懂的服务器推送技术(WebSocket和SSE)

一.什么是消息推送 推送的场景比较多&#xff0c;比如有人关注我的公众号&#xff0c;这时我就会收到一条推送消息&#xff0c;以此来吸引我点击打开应用。 消息推送(push)通常是指网站的运营工作等人员&#xff0c;通过某种工具对用户当前网页或移动设备APP进行的主动消息推送…

服务器推送技术

目录 1.前序 2.Ajax短轮询 3.Ajax长轮询 4.SSE 5.WebSocket 6.总结 1.前序 服务器推送技术&#xff1a;不用用户刷新发出请求&#xff0c;服务器主动发送实时信息到客户端。 因为HTTP协议是无状态&#xff0c;单向性的协议。 无状态是说客户端每一次请求都是全新的&…

网络编程五-服务器推送技术

目录 一、服务器推送技术 1、服务器推送技术的兴起 2、应用场景 二、Ajax短轮询 1、定义 2、特点 三、Comet 3.1 AJAX 的长轮询 1、定义 2、特点 3.2 SSE 1、定义 2、特点 四、WebSocket通信 1、什么是webSocket 2、特点 3、WebSocket通信握手 4、WebSocket通…

SecureFx连接Linux系统乱码

最近用SecureFx连接Ubuntu时&#xff0c;出现乱码&#xff0c;经过一番查询资料和实验最终得以解决&#xff0c;希望对大家有所帮助。 1、在选项中设置字符编码为UTF-8 Options -- Session Options -- Teminal -- Appearance 2、在选项的全局选项中找到Securefx的配置文件 Opt…

SecureCRT和SecureFx的使用

SecureCRT和SecureFx的使用 1.SecureCRT和SecureFx登陆中文乱码2.SecureFX打开两个窗口&#xff0c;即本地窗口和远程窗口 1.SecureCRT和SecureFx登陆中文乱码 SecureCRT与SecureFX的常规选项里面已经设置成了UTF-8&#xff0c;但是在SecureCRT中新建的中文文件夹&#xff0c;…

SecureCRT SecureFX中文乱码修改问题

1、一般解决方法&#xff1a;修改UFT-8 选项--会话选项--外观--字符编辑--选择UFT-8--确定【有可能需要重启一下CRT】 2、配置文件修改【好用】&#xff1a; 选项--全局选项--常规--配置文件夹【复制配置文件夹路径&#xff0c;选择要修改的ini文件】 将UTF8"00000000修改…

SecureFX传输速度一直是0

今天学往Linux里传jdk和tomcat时发现传输速度一直不动&#xff0c;检查了Linux的网络连接&#xff0c;也正常&#xff0c;如图&#xff1a; 而且连接ip也对&#xff0c;最后突然想到了权限问题&#xff0c;发现自己登录的不是root用户&#xff0c;又败给了自己...

安装SecureCRT和SecureFX踩过得坑

1.下载 给大家提供两种下载途径吧 1.官网可以下载到最新版的&#xff0c;弊端呢就是需要用邮箱注册一下&#xff0c;登录后才能下载&#xff0c;只有三十天的试用期&#xff0c;如果想长久使用的话还得破解一下&#xff0c;稍微的麻烦一丢丢。 2.https://pan.baidu.com/s/18OZ…

SecureFX之激活教程

SecureCRT激活请参考另一篇文章&#xff1a;SecureCRT激活教程 SecureFX激活教程开始&#xff1a; 步骤1&#xff1a;把激活软件放到该软件的安装目录下&#xff1a;&#xff08;如果找不到安装目录&#xff0c;找到该软件的桌面快捷方式&#xff0c;鼠标右键-----打开文件位…

SecureFx设置密钥登陆

SecureFx设置密钥登陆: 工具,创建公钥下一步密钥类型选RSA 为了更安全&#xff0c;可以设置通行短语 7、将identity.pub上的公钥放到服务器上 ①创建目录 /root/.ssh 并设置权限 [rootlocalhost ~]# mkdir /root/.ssh &#xff08;mkdir 命令来创建目录&#xff09; [ro…

解决SecureFX中文乱码的方法

SecureFX出现乱码&#xff0c;解决办法 1.点击Options选项&#xff0c;选择Global Options 2.点击打开Global Options窗口之后&#xff0c;在左边的General选项下方找到Configuration Path并点击&#xff0c;然后在右边找到路径并将它复制下来&#xff0c;如下图中所示。 3.路径…

解决SecureFX无法连接linux服务器

场景描述&#xff1a; 使用SecureFX无法连接linux服务器。 显示&#xff1a;由于目标计算机积极拒绝&#xff0c;无法连接 但使用它的配套软件SecureCRT&#xff0c;却可以进行连接。 问题分析&#xff1a; 既然CRT可以连接&#xff0c;说明Linux服务器本身是没有问题&#…

SecureFX_CRT安装

SecureFX_CRT_9安装和注册 SecureFX_CRT_9下载地址&#xff1a;暂时没放上去&#xff0c;最近有点忙&#xff0c;之后再完善下 一、安装 运行SecureFX_CRT安装程序,安装完之后先注册再运行程序 二、注册 注册SecureFX 1、进入安装的目录&#xff08;我的安装目录是…

SecureCRTSecureFX Portable远程连接Linux;上传下载文件

SecureCRT和SecureFX都是由VanDyke出品的SSH传输工具。 SecureCRT是一款非常好用的、支持多标签的SSH客户端&#xff0c;极大方便了管理多个SSH会话。 SecureFX则是一款专业而灵活、支持安全文件传输的SFTP客户端&#xff0c;也支持SCP、SSL等安全协议、以及传统的FTP协议。另…

SecureCRT$SecureFX的安装方法

SecureCRT$SecureFX的安装方法 SecureCRT$SecureFX下载地址 链接&#xff1a;https://pan.baidu.com/s/1SskwXkD6Cc1JKhvcQUIAIw 提取码&#xff1a;n1ql 安装过程 下载完成后&#xff0c;并解压SecureCRT&SecureFX Version.rar 运行安装程序scrt-sfx-x64.9.1.1.2638.…

SecureCRT和SecureFX

1.SecureCRT & SecureFX 介绍 1.1 SecureCRT SecureCRT 是一款终端仿真软件&#xff0c;和 Putty 的功能类似。 不过相比Putty比较简单的功能&#xff0c;SecureCRT 软件还是有许多的优势。比如可以一次创建多个 Tab Page&#xff0c;连接多台远程主机&#xff1b; Butto…

secureCRT及secureFX配置

secureCRT及secureFX配置 secureCRT安装卸载调节字体中文乱码 secureFX展示带.的文件展示带.的文件显示文件夹树右半边详细信息不见了 参考链接 secureCRT 安装 因为是需要注册的一般需要网上的注册码,可以用的,但是没有secureFX 见下链接 也可以直接去网上找那些自带改注册…

linux securefx 传输文件失败,解惑:如何使用SecureCRT上传和下载文件、SecureFX乱码问题...

解惑:如何使用SecureCRT上传和下载文件、SecureFX乱码问题 一、前言 很多时候在windows平台上访问Linux系统的比较好用的工具之一就是SecureCRT了,下面介绍一下这个软件的一些基本使用。 二、上传和下载文件 当我们登录到Linux系统之后最常用的操作就是上传和下载文件了。Sec…