长连接方式:以前一直局限在http长轮询阻塞、activex控件、flash控件、javaapplet 、websocket 考虑。 最近看到淘宝 增量订单同步的webapi(
http://open.taobao.com/doc/detail.htm?id=1029)才有了想法,想进一步了解此http长连接是如何实现,果然别有洞天。
目前此方式主要用于:web实时推送 、web桌面、twitter 、事件驱动(订阅、取消订阅)
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),或出错等事件
服务端代码 写道
package com.ist.manage.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.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);
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();
}
}
}
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();
}
}
}
RandomSender 写道
package com.thread;
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;
}
}
}
}
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侧展现。