ProtocolHandler接口有一个isCometSupported方法,Comet模式是一种服务器向客户端推数据的方式,由服务器端通过长连接向客户端推送数据,功能跟websocket类似的。
Tomcat的6,7,8.0等版本都支持Comet的,它的catalian.jar中的org.apache.catalina.comet包里面有Comet相关类。但是tomcat8.5版本不知道为什么就不支持comet了,它的catalina包下就没有comet包了。
所以本文实践环境为Eclipse +Tomcat8.0,实现Comet的客户端长轮询。
创建一个java web dynamic工程,并将Tomcat8.0的lib目录导入类路径中,需要应用catalina.jar中的CometProcessor,创建一个Servlet类:
import java.io.IOException;
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 CometServlet extends HttpServlet implements CometProcessor {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final Integer TIMEOUT = 60 * 1000;
@Override
public void destroy() {
}
@Override
public void init() throws ServletException {
}
@Override
public void event(CometEvent event) throws IOException, ServletException {
HttpServletRequest request = event.getHttpServletRequest();
HttpServletResponse response = event.getHttpServletResponse();
if (event.getEventType() == CometEvent.EventType.BEGIN) {
request.setAttribute("org.apache.tomcat.comet.timeout", TIMEOUT);
log("Begin for session: " + request.getSession(true).getId());
new CometHandler(response).process();
} else if (event.getEventType() == CometEvent.EventType.ERROR) {
log("Error for session: " + request.getSession(true).getId());
event.close();
} else if (event.getEventType() == CometEvent.EventType.END) {
log("End for session: " + request.getSession(true).getId());
event.close();
} else if (event.getEventType() == CometEvent.EventType.READ) {
throw new UnsupportedOperationException("This servlet does not accept data");
}
}
}
该Servlet实现CometProcessor后,就可以处理Comet请求了,在event事件中,根据请求状态交给Handler进行处理。定义CometHandler类如下:
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletResponse;
public class CometHandler {
private ServletResponse response;
public CometHandler(ServletResponse response){
this.response = response;
}
public void process(){
System.out.println("休眠6秒,然后返回响应数据");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//写入数据
try {
PrintWriter writer = response.getWriter();
System.out.println("write response info :hello coment");
writer.println("<h1>Beijing</h1><p>Sunny</p>");
writer.flush();
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
处理器休眠几秒后发送数据给客户端。
在web.xml中配置上述Servlet信息如下:
<servlet>
<servlet-name>cometServlet</servlet-name>
<servlet-class>com.CometServlet</servlet-class>
<!-- Change the above to use the other servlets -->
</servlet>
<servlet-mapping>
<servlet-name>cometServlet</servlet-name>
<url-pattern>/cometTest</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
在项目的WebContent目录下创建index.jsp文件,内容如下:
<%@page contentType="text/html" 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">
<title>Comet Test</title>
<SCRIPT TYPE="text/javascript">
function go(){
var url = "http://localhost/my_comet/cometTest"
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.setRequestHeader("Content-Type","application/x-javascript;");
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 200){
console.log("Receive response data."+request.responseText);
if (request.responseText) {
document.getElementById("cometTest").innerHTML = request.responseText;
}
}
go();
}
};
request.send(null);
}
</SCRIPT>
</head>
<body>
<h1>Rapid Fire Weather</h1>
<input type="button" onclick="go()" value="Go!"></input>
<div id="cometTest"></div>
</body>
</html>
客户端使用ajax轮询,一旦服务器端返回数据,立即进行下一轮循环,如果没有数据就阻塞等待。
Comet只有在NIO模式下才能启动,所以需要修改server.xml配置设置connetor为NIO模式:
<Connector URIEncoding="UTF-8" maxThreads="150" port="80"
protocol="org.apache.coyote.http11.Http11NioProtocol"
useBodyEncodingForURI="true"/>
发布项目并运行,页面点击“Go”按钮,浏览器就开始进行轮询请求。
参考下列链接,然后自定义CometHander处理请求,完成数据发送流程的。
https://www.ibm.com/developerworks/cn/web/wa-cometjava/
Tomcat8.5是不是废弃了Comet呢?客户端和服务器端全双工的通讯模式的技术还有websocket,tomcat8.5废弃Comet,大概是因为它支持websocket了,而这两种技术其实是一样的功能。
对于Comet,之所以想了解它,是因为我已经实践并应用过websocket了,对比了下它们能提供的功能,差异不大。而websocket似乎更普及一些,可能Comet的大势已去了吧。