设为首页 加入收藏

TOP

使用Tomcat实现基于iframe streaming的Comet聊天室(一)
2015-07-20 17:56:46 来源: 作者: 【 】 浏览:3
Tags:使用 Tomcat 实现 基于 iframe streaming Comet 聊天室

首先,无图无真相,先上图:

\

这是一个基于Comet实现的聊天室Demo,功能类似于QQ群聊。聊天过程中如果有新想消息,那么就需要服务器推送消息到浏览器,所以这里可以使用Comet技术。

Comet一般有两种实现方式:长轮询(long-polling)、流(streaming)。而本文中的这个Demo的实现方式是基于流(streaming),前端使用了一个隐藏的iframe,这也是比较常用的一种方式。不过由于使用iframe流,导致浏览器上面的进度一直在转,这是因为iframe一直在加载的原因,先不要在意这些细节。

Tomcat提供了Comet相关的API,用Servlet实现CometProcessor接口就可以很简单的实现Comet了。

1、准备工作

1.1、首先,需要配置Tomcat连接为NIO,否则无法使用Tomcat Comet。

Tomcat目录下conf/server.xml,protocol更改为org.apache.coyote.http11.Http11NioProtocol:


配置好后启动Tomcat应该是这样:

\

1.2、在开发过程中,需要用到Tomcat的catalina.jar包,在Tomcat的lib目录下。程序在Tomcat中运行时再去掉。

2、Java后台

2.1、CometServlet

这个Servlet是处理Comet Http长连接的Servlet,这个Servlet实现Tomcat提供的CometProcessor接口,通过event方法来处理Http长连接周期内的多种事件:

BEGIN事件:有新的HTTP连接;
END事件:连接关闭,例如浏览器关闭;
ERROR事件:连接错误,例如timeout。

有关事件更详细介绍在Tomcat官方文档中有:http://tomcat.apache.org/tomcat-7.0-doc/aio.html

public class CometServlet extends HttpServlet implements CometProcessor {
	
	// 所有正在等待响应的HTTP长连接
	private ArrayList
  
    connections = null;
	
	// 用于发送消息的线程
	private MessageSender messageSender = null;
	
	// 启动消息处理线程
	public void init() {
		connections = new ArrayList
   
    (); messageSender = new MessageSender(connections); Thread messageSenderThread = new Thread(messageSender); messageSenderThread.start(); } public void event(CometEvent event) throws IOException, ServletException { HttpServletResponse response = event.getHttpServletResponse(); response.setCharacterEncoding("UTF-8"); if (event.getEventType() == CometEvent.EventType.BEGIN) { System.out.println("BEGIN"); // 一段大于1024的字符串,针对某些浏览器缓存 PrintWriter out = response.getWriter(); StringBuilder sb = new StringBuilder(); for(int i = 0; i < 1024; i++) { sb.append('a'); } out.println("
    "); // 注意加上HTML注释 out.flush(); synchronized(connections) { connections.add(response); System.out.println("当前在线用户:" + connections.size()); } } else if (event.getEventType() == CometEvent.EventType.ERROR) { System.out.println("ERROR"); synchronized(connections) { connections.remove(response); System.out.println("当前在线用户:" + connections.size()); } event.close(); } else if (event.getEventType() == CometEvent.EventType.END) { System.out.println("END"); synchronized(connections) { connections.remove(response); System.out.println("当前在线用户:" + connections.size()); } event.close(); } } }
   
  

在这个Servlet中,ArrayList connections用于保存正在等待响应的HTTP长连接,是HttpServletResponse对象,可以理解为所有在线用户。在BEGIN事件中会向connections中添加一个连接,在END和ERROR事件中会将对应的连接删除。

在Servlet初始化init的时候,启动一个线程用于处理聊天消息,并把connections传过去。

在BEGIN事件中,先通过response的输出流输出了一段大于1024的字符串,这是由于浏览器的缓存原因,如果没有的话在某些浏览器下会有要等到流写到一定字节数后再显示的情况。这段字符串没有实际意义,所以可以随便写什么,但不要忘了加上HTML注释。

2.2、MessageSender

MessageSender是处理聊天消息的一个线程,实现Runnable接口。当有新的聊天信息时,它通过HttpServletResponse的输出流立即将信息发送到所有连接的客户端,没有新的信息则处于阻塞状态。

处理聊天消息的时候使用了java.util.concurrent中的阻塞队列ArrayBlockingQueue。ArrayBlockingQueue.take()方法用于获取并移除队列中的一个元素,当队列为空时该方法阻塞当前线程,直到有其他线程向这个队列中添加新元素。当然这里也可以用wait/notify来替代。

实际上可以将其理解成一个生产者消费者问题,有用户发送消息到服务器相当于生产一条消息,而这个线程将消息发送给所以用户相当于消费一条消息,而这个阻塞队列即是缓冲区。

public class MessageSender implements Runnable {
	
	// 所有正在等待响应的HTTP长连接
	private ArrayList
   
     connections;
	
	// 未发送给客户端的消息集合
	public static ArrayBlockingQueue
    
      messages = new ArrayBlockingQueue
     
      (10); public MessageSender(ArrayList
      
        connections) { this.connections = connections; } public void run() { while(true) { // 消息阻塞队列中获取一条消息,如果队列为空则阻塞 String message = null; try { message = messages.take(); } catch (InterruptedException e) { e.printStackTrace(); } // 给每个客户端发送消息 synchronized (connections) { for(HttpServletResponse response : connections) { try { PrintWriter out = response.getWriter(); // 输出一段脚本,调用JS将消息显示在页面上 out.println("<script>parent.addMsg('" + message + "
       
')"); out.flush(); } catch (IOException e) { e.printStackTrace(); } } } } } }

2.3、AjaxMessageServlet

这个Servlet用于处理用户发送信息的请求,这是一个普通的Http请求而不是长连接。点击页面中的“发送”按钮时,就会通过Ajax向这个Servlet提交聊天信息

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C++MFC编程笔记day08 MFC对话框的.. 下一篇C++中的四种转型操作符

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: