“服务器推”之Iframe配合comet实现

首先,通过tomcat的comet来实现服务器推的技术要在tomcat的Server.xml文件中修改一下设置:

将普通情况的

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

改为:
<Connector connectionTimeout="20000" port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443"/>

其次,服务器端使用实现了org.apache.catalina.comet.CometProcessor接口的HTTPservlet来处理,在其中实现这个接口的event方法,要加入tomcat的catalina.jar这个包(有可能待版本号),简单处理如下:
public class TestComet extends HttpServlet implements CometProcessor {
	
	private static final long serialVersionUID = 1L;


	private RandomSender randomSender = null;
	private final static int TIMEOUT = 60*1000;
	
	@Override
	public void event(final CometEvent event) throws IOException, ServletException {
		HttpServletRequest rq = event.getHttpServletRequest();
		HttpServletResponse rp = event.getHttpServletResponse();
		if(event.getEventType() == CometEvent.EventType.BEGIN){
			rq.setAttribute("org.apache.tomcat.comet.timeout", TIMEOUT);
			randomSender = new RandomSender(rp);
			Thread thread = new Thread(randomSender);
			thread.start();
		}else if(event.getEventType() == CometEvent.EventType.ERROR){
			event.close();
		}else if(event.getEventType() == CometEvent.EventType.END){
			event.close();
		}else if(event.getEventType() == CometEvent.EventType.READ){
			throw new UnsupportedOperationException("this servlet does not accept data");
		}
	}
}

public class RandomSender implements Runnable {
	
	private boolean running = true;
	private ServletResponse response;
	Random rand;
	
	public RandomSender(ServletResponse rq) {
		this.response  =rq;
		rand = new Random();
	}
	
	@Override
	public void run() {
		while(running){
			try {
				PrintWriter pw = response.getWriter();
				pw.println(rand.nextInt(10));
				pw.flush();
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}


}


这只是最基础的实现,如果要处理复杂一些的情况就多实现一些。服务器端实现关键点比较单一,就是使用PrintWriter pw = response.getWriter();往浏览器端写入东西,写入的东西是字符串文本还是脚本就看具体情况了。


一、我们通过XMLHttpRequest来实现
在browser端会这么写:
<script type="text/javascript">
	function go(){
		url = "http://localhost:8080/comet/cometTest";
		var request;
		request = new XMLHttpRequest();
		request.open("GET",url,true);
		request.setRequestHeader("Content-Type","applicatin/x-javascript;");
		request.onreadystatechange = function(){
			if(request.readyState == 3){
				if(request.status == 200){
					if(request.responseText){
						//这里需要注意的是:往后的每次数据都包含以前的数据,也就是说以前的数据还在request.responseText中
						document.getElementById("number").innerHTML = request.responseText;
					}
				}
			}
		};
		request.send(null);
	}
	
	</script>


然而,这代码只能在Firefox下能正常工作,因为request.readyState == 3时读取返回的数据在其他浏览器中没有被支持(IE也不例外),会报错,并且目前在用户群体中使用Firefox的比例非常小,IE还是主流的。所以这种方式局限明显。


二、Iframe极简单的实现
浏览器端:
<title>test comet</title>
<script type="text/javascript">
	//定义一个js函数,由服务断写到iframe里的js调用,但是这时,IE标签上的圆圈一直在转
	function changeNumber(num) {
		document.getElementById("number").innerHTML = num;
	}
</script>
</head>
<body>
	<h1>test comet</h1>
	<div id="number">1111</div>
	<iframe id="iframe" src="cometIframeTest"></iframe>
</body>




服务器端:
				PrintWriter pw = response.getWriter();
				pw.println("<script>parent.changeNumber("+rand.nextInt(10)+")</script>");
				pw.flush();


就是往iframe里写一段javascript脚本。但是这种情况,Firefox和IE的标签会一直转圈(进度条一直没完),不美观。另外,这种情况,如果往浏览器端写入字符文本,而不是脚本,那么在Firefox下会实时更新,但是在IE下要达到一定字节才会更新,并且写入的数据一直在里面,如果需要的话就做一下过滤。


三、Iframe改进版
解决IE下和Firefox下进度条的问题,我们采取往iframe里写入脚本的方式来实现
html页面:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>comet test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
	<div id="content">show the message</div>


<script type="text/javascript">


var comet = {
  connection   : false,
  iframediv    : false,


  initialize: function() {
    if (navigator.appVersion.indexOf("MSIE") != -1) {
      // IE
      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='IECometServlet'></iframe>";


    } else if (navigator.appVersion.indexOf("KHTML") != -1) {
      // KHTML
      comet.connection = document.createElement('iframe');
      comet.connection.setAttribute('id','iframe');
      comet.connection.setAttribute('src','IECometServlet');
      with (comet.connection.style) {
        position   = "absolute";
        left       = top   = "-100px";
        height     = width = "1px";
        visibility = "hidden";
      }
      document.body.appendChild(comet.connection);


    } else {
      // For other browser (Firefox...)
      comet.connection = document.createElement('iframe');
      comet.connection.setAttribute('id','iframe');
      with (comet.connection.style) {
        left       = top   = "-100px";
        height     = width = "1px";
        visibility = "hidden";
        display    = 'none';
      }
      comet.iframediv = document.createElement('iframe');
      comet.iframediv.setAttribute('src', 'IECometServlet');
      comet.connection.appendChild(comet.iframediv);
      document.body.appendChild(comet.connection);
    }
  },
  
  changeNumber: function (num) {
    document.getElementById('content').innerHTML = num;
  },


  onUnload: function() {
    if (comet.connection) {
      comet.connection = false;
    }
  }
}
if (window.attachEvent){
	window.attachEvent("onload",   comet.initialize);
	window.attachEvent("onunload", comet.onUnload);
}else{
	window.addEventListener("load", comet.initialize, false);
	window.addEventListener("unload", comet.onUnload, false);
}


</script>


</body>
</html>


服务器端:
package org.wz.comet.ie;


import java.io.IOException;
import java.util.ArrayList;


import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import org.apache.catalina.comet.CometEvent;
import org.apache.catalina.comet.CometProcessor;


public class IECometServlet extends HttpServlet implements CometProcessor {


	private static final long serialVersionUID = 1L;


	public static ArrayList<HttpServletResponse> connections = new ArrayList<HttpServletResponse>();


	public void init() throws ServletException {


	}


	public void destroy() {
		connections.clear();


	}


	public void event(CometEvent event) throws IOException, ServletException {
		HttpServletRequest request = event.getHttpServletRequest();
		HttpServletResponse response = event.getHttpServletResponse();
		if (event.getEventType() == CometEvent.EventType.BEGIN) {
			event.setTimeout(Integer.MAX_VALUE);
			MessageSender tp = new MessageSender(response);
			Thread t = new Thread(tp);
			t.start();
			synchronized (connections) {
				connections.add(response);
			}
		} else if (event.getEventType() == CometEvent.EventType.ERROR) {
			synchronized (connections) {
				connections.remove(response);
			}
			event.close();
		} else if (event.getEventType() == CometEvent.EventType.END) {
			synchronized (connections) {
				connections.remove(response);
			}
			event.close();
		} else if (event.getEventType() == CometEvent.EventType.READ) {
			event.close();
		}
	}
}

package org.wz.comet.ie;


import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;


import javax.servlet.ServletResponse;


public class MessageSender implements Runnable{


	private boolean running = true;
	private ServletResponse response;
	
	public MessageSender(ServletResponse response) {
		this.response = response;
	}
	
	@Override
	public void run() {
		while(running){
			PrintWriter writer;
			try {
				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.print("<script type=\"text/javascript\">");
				writer.println("comet.changeNumber('"+new Random().nextInt(100)+"');");
				writer.print("</script>");
				writer.print("</body>");
				writer.print("</html>");
				writer.flush();
			} catch (IOException e) {
				System.err.println("IOException------------------");;
			}
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				System.err.println("InterruptedException---------------------");
			}
		}
	}


}


但是这样的方式,IE和Firefox正常啦,但是chrome还是转圈的,找到解决方法了再来更新,如果有大神们找到请告诉一下小弟,谢谢!


参考资料:

点击打开链接

点击打开链接


特别感谢这位前辈的博客:点击打开链接


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值