- 浏览: 17515 次
- 性别:
- 来自: 北京
最新评论
-
Core_Star:
例子没跑成功,只有当writer被close掉的时候信息才发送 ...
Comet基于iframe的服务器推送(Server Push)例子 -
hankuksui:
不知道引用哪些jar包啊?
Comet基于iframe的服务器推送(Server Push)例子 -
ariesmonster:
如果是在Eclipse中运行例子,需要修改Servers工程下 ...
Comet基于iframe的服务器推送(Server Push)例子 -
LoriSun:
多谢楼主的分享, 我试验了一下demo, 但是没有测试成功. ...
Comet基于iframe的服务器推送(Server Push)例子
服务器推送技术(Server Push)是最近Web技术中最热门的一个流行术语,它的别名叫Comet(彗星)。它是继AJAX之后又一个倍受追捧的Web技术。Comet有时也称反向 Ajax 或服务器端推技术(server-side push)。其思想很简单:将数据直接从服务器推到浏览器,而不必等到浏览器请求数据。听起来简单,但是如果熟悉 Web 应用 程序,尤其是 HTTP 协议,那么您就会知道,这绝不简单。实现 Comet 风格的 Web 应用程序,同时保证在浏览器和服务器上的可伸缩性,这只是在最近几年才成为可能。目前一些主流网站都有类似的原理,例如:webQQ、开心网、白社会等等,它们中消息动态都是采用类似的技术,也许具体实现方式不一样。目前大概有三种实现方式:基于长轮询(long polling)、基于iframe“、基于流(stream)三种实现comet的方式。
下面是在Tomcat6.x的基础上实现基于iframe的comet聊天室:
仅仅是一个Demo,可以从index.jsp?name=yourNickName进入,实现了上线,下线通知,群发,消息功能,性能还没有测试...
目前能够正常在IE,FireFox和Chrome下正常运行,但在Chrome下会一直加载不完,不知道有没有办法解决?还有不知道能不能在此基础之上搞一个基于iframe的Comet工具,包括前台js和后台java.
首先修改Tomcat配置文件
修改server.xml中<Connector connectionTimeout="20000" port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443"/>,底层用NIO实现的Http连接器。如果是在Eclipse中运行例子,需要修改Servers下的Tomcat v6.0-config中的server.xml.
index.jsp
ChatServlet.java
在浏览器中浏览
http://localhost:8080/CometDemo/index.jsp?name=user1
http://localhost:8080/CometDemo/index.jsp?name=user2
可以看到效果
下面是在Tomcat6.x的基础上实现基于iframe的comet聊天室:
index.jsp:聊天室页面
ChatServlet.java:实现了长连接的Servlet
MessageServlet.java:消息接受Servert
仅仅是一个Demo,可以从index.jsp?name=yourNickName进入,实现了上线,下线通知,群发,消息功能,性能还没有测试...
目前能够正常在IE,FireFox和Chrome下正常运行,但在Chrome下会一直加载不完,不知道有没有办法解决?还有不知道能不能在此基础之上搞一个基于iframe的Comet工具,包括前台js和后台java.
首先修改Tomcat配置文件
修改server.xml中<Connector connectionTimeout="20000" port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443"/>,底层用NIO实现的Http连接器。如果是在Eclipse中运行例子,需要修改Servers下的Tomcat v6.0-config中的server.xml.
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="jquery-1.4.2.min.js"></script> <link rel="stylesheet" href="960.css" /> <title>Comet Chat Demo</title> <script type="text/javascript"> var server = 'http://localhost:8080/CometDemo/ChatComet?name=<%=request.getParameter("name")%>'; var comet = { connection : false, iframediv : false, initialize: function() { if (navigator.appVersion.indexOf("MSIE") != -1) { comet.connection = new ActiveXObject("htmlfile"); comet.connection.open(); comet.connection.write("<html>"); comet.connection.write("<script>document.domain = '"+document.domain+"'"); comet.connection.write("</html>"); comet.connection.close(); comet.iframediv = comet.connection.createElement("div"); comet.connection.appendChild(comet.iframediv); comet.connection.parentWindow.comet = comet; comet.iframediv.innerHTML = "<iframe id='comet_iframe' src='"+server+"'></iframe>"; } else if (navigator.appVersion.indexOf("KHTML") != -1) { comet.connection = document.createElement('iframe'); comet.connection.setAttribute('id', 'comet_iframe'); comet.connection.setAttribute('src', server); with (comet.connection.style) { position = "absolute"; left = top = "-100px"; height = width = "1px"; visibility = "hidden"; } document.body.appendChild(comet.connection); } else { comet.connection = document.createElement('iframe'); comet.connection.setAttribute('id', 'comet_iframe'); with (comet.connection.style) { left = top = "-100px"; height = width = "1px"; visibility = "hidden"; display = 'none'; } comet.iframediv = document.createElement('iframe'); comet.iframediv.setAttribute('src', server); comet.connection.appendChild(comet.iframediv); document.body.appendChild(comet.connection); } }, //添加用户 newUser:function(data){ var list = document.getElementById('userList'); var li = document.createElement('li'); li.setAttribute("id","u1"+data); li.innerHTML = data; list.appendChild(li); var user = document.getElementById('user'); var option = document.createElement('option'); option.setAttribute("id","u2"+data); option.innerHTML = data; user.appendChild(option); }, //删除用户 deleteUser:function(data){ $('#u1'+data).remove(); $('#u2'+data).remove(); }, //添加公共消息 newMessage:function(data){ var list = document.getElementById('messageList'); var li = document.createElement('li'); li.innerHTML = data; list.appendChild(li); }, //添加私人消息 privateMessage:function(data){ var list = document.getElementById('privateMessage'); var li = document.createElement('li'); li.innerHTML = data; list.appendChild(li); }, //退出 onUnload: function() { if (comet.connection) { comet.connection = false; } } }//comet end if (window.addEventListener) { window.addEventListener("load", comet.initialize, false); window.addEventListener("unload", comet.onUnload, false); } else if (window.attachEvent) { window.attachEvent("onload", comet.initialize); window.attachEvent("onunload", comet.onUnload); } </script> </head> <body> <script type="text/javascript"> function sendAll(){ var list = document.getElementById('privateMessage'); var li = document.createElement('li'); li.innerHTML = "I said to "+$("#user").val()+": " + $("#message").val(); list.appendChild(li); $.ajax({ type: "POST", url: "MessageServlet", data: "message="+$("#message").val()+"&user="+$("#user").val()+"&from=<%=request.getParameter("name")%>" }); } </script> <div class="container_12"> <div class="grid_10"> <div>公共聊天</div> <div id="messageList" style="height:250px;overflow:scroll;border:solid 1px black;"> </div> <br/> <div>个人聊天</div> <div id="privateMessage" style="height:150px;overflow:scroll;border:solid 1px black;"> </div> <br/> <div> <select id="user" style="width:100px;overflow:scroll;"> <option value="all">All</option> </select> <input type="text" id="message" size="40"></input> <input type="button" value="发言" onclick="sendAll()"> </div> </div> <div class="grid_2"> <h3>用户列表</h3> <ol id="userList"> </ol> </div> </div> </body> </html>
ChatServlet.java
package demo; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.CometEvent; import org.apache.catalina.CometProcessor; /** * * @author Aries Zhao * */ public class ChatServlet extends HttpServlet implements CometProcessor { private static final long serialVersionUID = -3667180332947986301L; // <用户,长连接> protected static Map<String, HttpServletResponse> connections = new HashMap<String, HttpServletResponse>(); // 消息推送线程 protected static MessageSender messageSender = null; public void init() throws ServletException { // 启动消息推送线程 messageSender = new MessageSender(); Thread messageSenderThread = new Thread(messageSender, "MessageSender[" + getServletContext().getContextPath() + "]"); messageSenderThread.setDaemon(true); messageSenderThread.start(); } public void destroy() { connections.clear(); messageSender.stop(); messageSender = null; } public void event(CometEvent event) throws IOException, ServletException { HttpServletRequest request = event.getHttpServletRequest(); HttpServletResponse response = event.getHttpServletResponse(); // 昵称 String name = request.getParameter("name"); if (name == null) { return; } if (event.getEventType() == CometEvent.EventType.BEGIN) { // Http连接空闲超时 event.setTimeout(Integer.MAX_VALUE); log("Begin for session: " + request.getSession(true).getId()); // 创建Comet Iframe PrintWriter writer = response.getWriter(); writer .println("<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">"); writer .println("<html><head><script type=\"text/javascript\">var comet = window.parent.comet;</script></head><body>"); writer.println("<script type=\"text/javascript\">"); writer.println("var comet = window.parent.comet;"); writer.println("</script>"); writer.flush(); // for chrome if (request.getHeader("User-Agent").contains("KHTML")) { for (int i = 0; i < 100; i++) { writer.print("<input type=hidden name=none value=none>"); } writer.flush(); } // 欢迎信息 writer.print("<script type=\"text/javascript\">"); writer.println("comet.newMessage('Hello " + name + ", Welcome!');"); writer.print("</script>"); writer.flush(); // 通知其他用户有新用户登陆 if (!connections.containsKey(name)) { messageSender.login(name); } // 推送已经登陆的用户信息 for (String user : connections.keySet()) { if (!user.equals(name)) { writer.print("<script type=\"text/javascript\">"); writer.println("comet.newUser('" + user + "');"); writer.print("</script>"); } } writer.flush(); synchronized (connections) { connections.put(name, response); } } else if (event.getEventType() == CometEvent.EventType.ERROR) { log("Error for session: " + request.getSession(true).getId()); synchronized (connections) { connections.remove(name); } event.close(); } else if (event.getEventType() == CometEvent.EventType.END) { log("End for session: " + request.getSession(true).getId()); messageSender.logout(name); synchronized (connections) { connections.remove(name); } PrintWriter writer = response.getWriter(); writer.println("</body></html>"); event.close(); } else if (event.getEventType() == CometEvent.EventType.READ) { InputStream is = request.getInputStream(); byte[] buf = new byte[512]; do { int n = is.read(buf); // can throw an IOException if (n > 0) { log("Read " + n + " bytes: " + new String(buf, 0, n) + " for session: " + request.getSession(true).getId()); } else if (n < 0) { return; } } while (is.available() > 0); } } // 发送消息给所有人 public static void send(String message) { messageSender.send("*", message); } // 向某个连接发送消息 public static void send(String name, String message) { messageSender.send(name, message); } public class MessageSender implements Runnable { protected boolean running = true; protected Map<String, String> messages = new HashMap<String, String>(); public MessageSender() { } public void stop() { running = false; } // 新用户登陆 public void login(String name) { synchronized (messages) { messages.put("Login", name); messages.notify(); } } // 用户下线 public void logout(String name) { synchronized (messages) { messages.put("Logout", name); messages.notify(); } } // 发送消息 public void send(String user, String message) { synchronized (messages) { messages.put(user, message); messages.notify(); } } public void run() { while (running) { if (messages.size() == 0) { try { synchronized (messages) { messages.wait(); } } catch (InterruptedException e) { // Ignore } } synchronized (connections) { synchronized (messages) { // 推送消息队列中的消息 for (Entry<String, String> message : messages .entrySet()) { if (message.getKey().equals("Login")) {// 新用户登陆 log(message.getValue() + " Login"); for (HttpServletResponse response : connections .values()) { try { PrintWriter writer = response .getWriter(); writer .print("<script type=\"text/javascript\">"); writer .println("comet.newMessage('Welcome " + message.getValue() + " !');"); writer.println("comet.newUser('" + message.getValue() + "');"); writer.print("</script>"); writer.flush(); } catch (IOException e) { log("IOExeption execute command", e); } } } else if ("Logout".equals(message.getKey())) {// 用户退出 log(message.getValue() + " Logout"); for (HttpServletResponse response : connections .values()) { try { PrintWriter writer = response .getWriter(); writer .print("<script type=\"text/javascript\">"); writer.println("comet.newMessage('88, " + message.getValue() + "');"); writer.println("comet.deleteUser('" + message.getValue() + "');"); writer.print("</script>"); writer.flush(); } catch (IOException e) { log("IOExeption execute command", e); } } } else if ("*".equals(message.getKey())) {// 群发消息 log("Send message: " + message.getValue() + " to everyone."); for (HttpServletResponse response : connections .values()) { try { PrintWriter writer = response .getWriter(); writer .print("<script type=\"text/javascript\">"); writer.println("comet.newMessage('" + message.getValue() + "');"); writer.print("</script>"); writer.flush(); } catch (IOException e) { log("IOExeption execute command", e); } } } else {// 向某人发信息 try { HttpServletResponse response = connections .get(message.getKey()); PrintWriter writer = response.getWriter(); writer .print("<script type=\"text/javascript\">"); writer.println("comet.privateMessage('" + message.getValue() + "');"); writer.print("</script>"); writer.flush(); } catch (IOException e) { log("IOExeption sending message", e); } } // 从消息队列中删除消息 messages.remove(message.getKey()); } } } } } } }
在浏览器中浏览
http://localhost:8080/CometDemo/index.jsp?name=user1
http://localhost:8080/CometDemo/index.jsp?name=user2
可以看到效果
- CometDemo.zip (77.7 KB)
- 下载次数: 322
- CometDemo.war (75 KB)
- 下载次数: 201
评论
4 楼
Core_Star
2012-08-06
例子没跑成功,只有当writer被close掉的时候信息才发送到客户端
3 楼
hankuksui
2012-03-23
不知道引用哪些jar包啊?
2 楼
ariesmonster
2010-12-14
如果是在Eclipse中运行例子,需要修改Servers工程下的Tomcat v6.0-config中的server.xml,而不是tomcat下的conf/server.xml.或者可以部署war包试试看,在浏览器中浏览
http://localhost:8080/CometDemo/index.jsp?name=user1
http://localhost:8080/CometDemo/index.jsp?name=user2
就可以看到效果。
个人感觉长轮询只是感觉像是server push,但本质还是要不停的向服务器发送请求,如果间隔时间太短,还是有大量的通信,服务器压力依然很大。
http://localhost:8080/CometDemo/index.jsp?name=user1
http://localhost:8080/CometDemo/index.jsp?name=user2
就可以看到效果。
个人感觉长轮询只是感觉像是server push,但本质还是要不停的向服务器发送请求,如果间隔时间太短,还是有大量的通信,服务器压力依然很大。
1 楼
LoriSun
2010-12-14
多谢楼主的分享, 我试验了一下demo, 但是没有测试成功. 我也修改了Tomcat的请求处理协议.
同时我也看了一下 Web2.qq.com 它采用的是Ajax 模拟长连接(long polling).
首先浏览器端发出Ajax请求(模拟长连接) : poll2. 如果server端有针对browser的更新, Server返回需要更新的内容, 浏览器端作出相应的更新; 或当这个请求长时间未获得要更新的内容, server端也会返回一个终止请求的信息(类似超时了), 浏览器端不坐任何更新. 每当poll2请求结束, 就马上再次发出这个Ajax请求; 这样周而复始, 貌似长连接, 貌似是Server push.
Dwr 中的 Reverted Ajax 只是在浏览器端定期发送请求到server, 也是我们项目中所用到的, 通过调整发起请求的频率事实性也还算可以的.
DWR:===============
https://epblnx1d.nam.nsroot.net:8191/pfPortal/dwr/call/plainpoll/ReverseAjax.dwr
<init-param>
<param-name>
org.directwebremoting.extend.ServerLoadMonitor</param-name>
<param-value>org.directwebremoting.impl.PollingServerLoadMonitor</param-value>
</init-param>
WEBQQ:=============
http://d.web2.qq.com/channel/poll2?clientid=13480813&psessionid=8368046764001e636f6e6e7365727665725f77656271714031302e3132382e36362e313132000011e8000001190162005d10446d0000000a40574e4c4d72307148656d00000030d2ff13dbfac5cddc5bff71315a52ac83ffde2a6e49fbc74875ba6a88b8efd63da4c8ab0c353a95693792c3ab1f9d69d2&t=1292314025483&vfwebqq=d2ff13dbfac5cddc5bff71315a52ac83ffde2a6e49fbc74875ba6a88b8efd63da4c8ab0c353a95693792c3ab1f9d69d2
同时我也看了一下 Web2.qq.com 它采用的是Ajax 模拟长连接(long polling).
首先浏览器端发出Ajax请求(模拟长连接) : poll2. 如果server端有针对browser的更新, Server返回需要更新的内容, 浏览器端作出相应的更新; 或当这个请求长时间未获得要更新的内容, server端也会返回一个终止请求的信息(类似超时了), 浏览器端不坐任何更新. 每当poll2请求结束, 就马上再次发出这个Ajax请求; 这样周而复始, 貌似长连接, 貌似是Server push.
Dwr 中的 Reverted Ajax 只是在浏览器端定期发送请求到server, 也是我们项目中所用到的, 通过调整发起请求的频率事实性也还算可以的.
DWR:===============
https://epblnx1d.nam.nsroot.net:8191/pfPortal/dwr/call/plainpoll/ReverseAjax.dwr
<init-param>
<param-name>
org.directwebremoting.extend.ServerLoadMonitor</param-name>
<param-value>org.directwebremoting.impl.PollingServerLoadMonitor</param-value>
</init-param>
WEBQQ:=============
http://d.web2.qq.com/channel/poll2?clientid=13480813&psessionid=8368046764001e636f6e6e7365727665725f77656271714031302e3132382e36362e313132000011e8000001190162005d10446d0000000a40574e4c4d72307148656d00000030d2ff13dbfac5cddc5bff71315a52ac83ffde2a6e49fbc74875ba6a88b8efd63da4c8ab0c353a95693792c3ab1f9d69d2&t=1292314025483&vfwebqq=d2ff13dbfac5cddc5bff71315a52ac83ffde2a6e49fbc74875ba6a88b8efd63da4c8ab0c353a95693792c3ab1f9d69d2
相关推荐
在Web开发中,服务器向客户端实时推送数据是常见...在名为"serverPush"的压缩包文件中,可能包含了实现上述各种服务器推送方式的JAVA代码示例,开发者可以通过阅读和学习这些代码,进一步理解并掌握服务器推送的技术。
Comet技术是一种基于HTTP长连接的Web实时通信技术,它允许服务器向客户端主动推送数据,而无需客户端发起新的请求。这种技术打破了传统的HTTP请求-响应模型,极大地提升了Web应用的实时性和交互性,尤其适用于股票...
ASP.NET Comet是一个技术概念,它涉及到了Web应用程序中的实时通信,特别是服务器向客户端推送数据的能力。在传统的HTTP协议中,服务器通常在客户端发起请求时才响应,而在Comet模式下,服务器可以保持一个连接开放...
3. **Comet技术**:Comet是长轮询的一种抽象,它包括了多种实现方式,如流、隐藏IFrame和JSONP等,目的是实现服务器到浏览器的推送。 4. **Servlet与WebSocket结合**:在JavaWeb应用中,CharServletPush可能表示一...
"消息推送"是Comet的核心特性,服务器能够主动将新的消息推送给客户端,无需客户端频繁地轮询请求,从而提高了效率并降低了服务器压力。 Comet技术通常有几种实现方式,包括长轮询(Long Polling)、iframe、流...
ASP.NET 消息推送技术是Web开发中的一个重要概念,它允许服务器主动向客户端发送数据,而无需客户端(如浏览器)不断地发起请求。这种技术在实时性要求高的应用场景中非常关键,比如聊天应用、股票更新、在线游戏等...
Pushlet和Comet技术是Web服务器向浏览器推送数据的两种策略,主要用于实现实时的、双向的通信。这两种技术在传统的HTTP协议基础上进行了扩展,克服了HTTP请求-响应模型的限制,使得服务器能够主动向客户端推送数据,...
服务器可以不断地向这个隐藏的IFrame推送数据,而JavaScript则可以处理这些数据并更新主页面。这种方式可以持续不断地更新信息,而不会被浏览器关闭或网络异常打断。 知识点五:TMN管理模型与实时网络管理框架的...
Comet是一种基于HTTP长连接的服务器推送技术,旨在提供实时的Web应用程序交互。它与AJAX类似,但不同之处在于,AJAX通常依赖于短连接和异步请求,而Comet则保持一个开放的HTTP连接,允许服务器主动向客户端推送数据...
网易邮箱使用Comet技术来实现其Webim(邮箱聊天)和Pushmail(邮件推送)等项目。这些项目需要实时更新和即时推送信息给用户,因此选择Comet作为技术支持。 5. 如何选择适合的方案: 当选择适合的Comet实现方案时...
DWR2.x版本引入了反推(Reverse Ajax)技术,也被称为服务器端推送(Server-Side Push),它打破了传统Ajax模式中由客户端发起请求、服务器响应的单向通信模式,实现了服务器端可以主动将数据推送到客户端的功能。...
- **使用Server Push技术**:例如Comet技术,可以让服务器主动推送数据到客户端。 - **选择合适的框架**:根据项目需求选择合适的框架,如DWR、Dojo Toolkit等。 ##### 3.2 实现案例 - **即时股市报价**:可以采用...
- **Comet**:一种实现Server Push的技术,通过对HTTP连接的长期保持来实现服务器向客户端推送数据。 - **WebSocket**:提供了全双工的通信通道,可以实现客户端和服务器之间的双向通信。 #### 六、应用实例 - ...
反向Ajax技术,又称为服务器推技术(server push),是一种允许服务器主动向客户端发送数据的技术。与传统Ajax技术的客户端拉数据(客户端通过定时向服务器发送请求来获取最新数据)不同,反向Ajax技术中,服务器能够...