COMET HTTP长连接,实现服务器推(server push),服务以事件(event)方式把消息推送给客户端,来解决客户端定时、频繁访问服务器的问题。
comet长连接有两种方式:长轮询方式(long-polling)和流方式(streaming)。
长轮询方式:HTTP的连接保持,服务端会阻塞请求,直到服务端有事件触发 OR 超时。客户端在收到相应后,再次发送请求建立连接。
流方式:服务器推送数据给客户端,但是不关闭连接,保持连接,直至超时。超时后客户端关闭连接,同时重新建立连接。Java 通过它的NIO库提供非阻塞IO处理Comet
实现:
1、支持COMET的服务器有:Tomcat 6.0.14 和Jetty 6.1.14 以后版本。
2、修改Tomcat配置文件conf/server.xml,使支持启动异步版本的IO连接器。
<Connector connectionTimeout="20000" port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443"/>
3、Comet API 此为Tomcat lib/catalina.jar
4、编写servlet,通过servlet实现 CometProcessor接口中的event() 方法。此方法中分别处理连接开始(BEGIN)、新数据可用(READ),连接结束(END),或出错等事件
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.CometEvent;
import org.apache.catalina.CometProcessor;
import org.apache.log4j.Logger;
import com.thread.RandomSender;
public class TestComet extends HttpServlet implements CometProcessor{
private static Logger logger = Logger.getLogger(TestComet.class);
private RandomSender randomSender = null;
private static final Integer TIMEOUT = 60*1000;
public void event(CometEvent event) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest request = event.getHttpServletRequest();
HttpServletResponse response =event.getHttpServletResponse();
logger.info("Test Comet http长连接测试...");
if(event.getEventType() == CometEvent.EventType.BEGIN){
request.setAttribute("org.apache.tomcat.comet.timeout", TIMEOUT);
randomSender = new RandomSender(request.getParameter("requestInfo"),response);
Thread thread = new Thread(randomSender);
thread.start();
}else if(event.getEventType() == CometEvent.EventType.READ){
throw new UnsupportedOperationException("this servlet dose not accept data");
}else if(event.getEventType() == CometEvent.EventType.END){
event.close();
}else if(event.getEventType() == CometEvent.EventType.ERROR){
event.close();
}
}
}
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.log4j.Logger;
import com.sun.org.apache.bcel.internal.generic.NEW;
public class RandomSender implements Runnable {
private static Logger logger = Logger.getLogger(RandomSender.class);
protected boolean running = true;
private ServletResponse response;
private String reqInfo;
Random rand;
String tmpString = "";
public RandomSender(String reqInfo,ServletResponse response){
this.response = response;
this.reqInfo = reqInfo;
logger.info("接收注册请求包信息:"+reqInfo);
rand = new Random();
}
public void run() {
// TODO Auto-generated method stub
PrintWriter out = null;
while (running) {
try {
out = response.getWriter();
logger.info("线程产生随机数..."+Thread.currentThread().getName());
tmpString = "random:"+rand.nextInt(10);
out.println(tmpString);
out.flush();
response.flushBuffer();
Thread.sleep(3000);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (NullPointerException e) {
// TODO: handle exception
e.printStackTrace();
logger.info("客户端关闭,捕获空指针异常");
running = false;
}
}
}
}
客户端代码:采用httpclient4.2.1.jar
package com.http.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.log4j.Logger;
public class HttpLongClient {
private static Logger logger = Logger.getLogger(HttpClient.class);
/**
* 请求返回状态码code 和 结果 result
*/
public static Map<String, Object> getHttpByPost(String url,
String[]... params) {
Map<String, Object> map = new HashMap<String, Object>();
org.apache.http.client.HttpClient client = new DefaultHttpClient();
// client.getParams().setIntParameter("http.socket.timeout", 10000);
client.getParams().setParameter("Connection", "Keep-Alive");
HttpPost post = new HttpPost(url);
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
StringBuilder builder = new StringBuilder();
String result = null;
String responseStr = "";
int responseCode = 0;
if(params!=null){
for (String[] param : params) {
nvps.add(new BasicNameValuePair(param[0], param[1]));
}
}
try {
post.setEntity((HttpEntity) new UrlEncodedFormEntity(nvps, "UTF-8"));
HttpResponse response = client.execute(post);
map = doResponse(response);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return map;
}
public static Map<String, Object> getHttpByGet(String url) {
Map<String, Object> map = new HashMap<String, Object>();
org.apache.http.client.HttpClient client = new DefaultHttpClient();
// client.getParams().setIntParameter("http.socket.timeout", 10000);
HttpGet get = new HttpGet(url);
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
try {
logger.error("url:"+url);
HttpResponse response = client.execute(get);
map = doResponse(response);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return map;
}
private static Map<String, Object> doResponse(HttpResponse response){
Map<String, Object> map = new HashMap<String, Object>();
String line = "";
String result = null;
String responseStr = "";
int responseCode = 0;
try {
HttpEntity entity = response.getEntity();
BufferedReader reader = new BufferedReader(new InputStreamReader(
entity.getContent(), "UTF-8"), 8 * 1024);
StringBuilder builder = new StringBuilder();
responseCode = response.getStatusLine().getStatusCode();
switch (responseCode) {
// 请求成功
case HttpURLConnection.HTTP_OK:
while ((line = reader.readLine()) != null) {
builder.append(line);
}
responseStr = builder.toString();
result = "请求成功";
break;
case HttpURLConnection.HTTP_BAD_REQUEST:
while ((line = reader.readLine()) != null) {
builder.append(line);
}
responseStr = builder.toString();
result = "错误请求";
break;
case HttpURLConnection.HTTP_INTERNAL_ERROR:
result = "服务器端错误";
break;
case HttpURLConnection.HTTP_NOT_FOUND:
result = "未找到指定的网址";
break;
case HttpURLConnection.HTTP_BAD_GATEWAY:
result = "请求超时";
break;
default:
break;
}
} catch (Exception e) {
e.printStackTrace();
map.put("code", HttpURLConnection.HTTP_BAD_GATEWAY);
map.put("result", "无法连接网络,请检查网络设置");
map.put("response", "");
}finally{
map.put("code", responseCode);
map.put("result", result);
map.put("response", responseStr);
logger.info("result:"+result);
logger.info("responseStr:"+responseStr);
logger.info("responseCode:"+responseCode);
}
return map;
}
public static void main(String[] args) throws Exception {
HttpLongClient client = new HttpLongClient();
String url = "http://127.0.0.1:8080/SSODemo/services/rest"; //http请求所访问的地址
url="http://localhost:8080/testComet.do";
Map<String, Object> data = null;
String requestInfo="sdfsdf";
String[] params = new String[]{"requestInfo",requestInfo};
//发送请求包
data = client.getHttpByPost(url, params);
String responseInfo = (String) data.get("response");
logger.error("resp:");
logger.error(responseInfo);
}
}
客户端也可以采用ajax ,直接在web侧展现。
本文深入探讨了HTTP长连接技术在实时推送、web桌面、Twitter等场景的应用,详细介绍了两种实现方式:长轮询和流方式。通过使用Comet API,实现了服务器推送数据给客户端的功能,保持了连接的持久性,提高了效率。文中还提供了Tomcat服务器的配置、Comet API的使用以及客户端的HTTP请求代码示例。
1345

被折叠的 条评论
为什么被折叠?



