即利用socket发送http请求,可以说是实现了http的长连接
本人也是第一次写socket的连接池,所以把代码发出来,希望有共同研究方向的同行能帮我指正不足之处。
1.池成员数据结构
package com.socket.pool; import java.net.Socket; public class SocketMember { //socket对象 private Socket socket; //是否正被使用 private boolean inUse=false; public Socket getSocket() { return socket; } public void setSocket(Socket socket) { this.socket = socket; } public boolean isInUse() { return inUse; } public void setInUse(boolean inUse) { this.inUse = inUse; } }
2.socket连接池类
package com.socket.pool; import java.io.IOException; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class SocketPool { //host private String host; //port private int port; //初始化socket数 private int initSize=5; //最大socket数 private int maxSize=5; //socket对象容器 private List<SocketMember> socketContainer=new ArrayList<SocketMember>(initSize); public SocketPool(String host,int port){ this.host=host; this.port=port; buildPoolPart(); } //给socket容器增加成员 一次增加initSize个成员 private List<SocketMember> buildPoolPart(){ List<SocketMember> poolPart=new ArrayList<SocketMember>(initSize); SocketMember member=null; for(int i=0;i<initSize;i++){ Socket socket=null; try { socket = new Socket(host, port); } catch (IOException e) { e.printStackTrace(); } member=new SocketMember(); member.setSocket(socket); poolPart.add(member); } if(poolPart.size()>0){ socketContainer.addAll(poolPart); }else{ try { throw new Exception("扩大池容量失败"); } catch (Exception e) { e.printStackTrace(); } } return poolPart; } //获取池中空闲的socket 已做线程安全处理 public SocketMember getMemberFromPool(){ SocketMember member=null; //同步块获取对象 synchronized (this) { for(int i=0;i<socketContainer.size();i++){ SocketMember memberInPool=socketContainer.get(i); boolean inUse=memberInPool.isInUse(); if(inUse==false){ memberInPool.setInUse(true); member=memberInPool; System.out.println("成功获取对象,在池中的位置为:"+i); break; } } //pool中没有空余 if(member==null){ if(socketContainer.size()<maxSize){ //扩大池容量 List<SocketMember> newPoolPart=buildPoolPart(); //从新扩大的部分拿出一个来用 member=newPoolPart.get(0); member.setInUse(true); System.out.println("成功扩大池容量,当前size为:"+socketContainer.size()); } } } //如果超过最大容量 等待 递归 if(member==null){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } member=getMemberFromPool(); } return member; } public int getInitSize() { return initSize; } public void setInitSize(int initSize) { this.initSize = initSize; } public int getMaxSize() { return maxSize; } public void setMaxSize(int maxSize) { this.maxSize = maxSize; } public List<SocketMember> getSocketContainer() { return socketContainer; } public String getHost() { return host; } public int getPort() { return port; } }
3.守护线程
package com.socket.pool; import java.io.IOException; import java.net.Socket; import java.util.List; /** * 保持池中的socket一直存活 每隔一段时间遍历池检查socket对象 如果已连接那么发送心跳包 如果已断开 那么重新建立连接 * @ClassName: DaemonThread * @Description: TODO * @Copyright: Copyright (c) 2016 * @Company: 深圳市梦网科技股份有限公司 * @author hxq * @date 2016-8-1 上午10:26:43 * @version V1.0 */ public class DaemonThread implements Runnable{ private SocketPool pool; public DaemonThread(SocketPool pool){ this.pool=pool; } public void run() { List<SocketMember> container=pool.getSocketContainer(); while(true){ //10秒检查一次 try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } //遍历检查是否连接 for(int i=0;i<container.size();i++){ SocketMember member=container.get(i); Socket socket=member.getSocket(); //给此socket加锁 synchronized (socket) { if(!member.isInUse()){ if(socket.isConnected()){ //如果连接发送心跳包 KeepAliveExcutor excutor=new KeepAliveExcutor(member); excutor.request(); }else{ //如果失败重新建立socket try { socket=new Socket(pool.getHost(), pool.getPort()); member.setSocket(socket); } catch (IOException e) { e.printStackTrace(); } System.out.println(socket.getLocalPort()+" 断线重连"); } } } //System.out.println(socket.getLocalPort()+" 状态"+socket.isConnected()); } } } } /** * 发送心跳包的executor * @ClassName: KeepAliveExcutor * @Description: TODO * @Copyright: Copyright (c) 2016 * @Company: 深圳市梦网科技股份有限公司 * @author hxq * @date 2016-8-16 下午02:16:32 * @version V1.0 */ class KeepAliveExcutor extends AbstractSocketExecutor{ public KeepAliveExcutor(SocketMember socketMember) { super(socketMember); } public void request(){ String request=SocketPoolUtil.getNowTimestamp(); super.request(request, "utf-8"); } }
5.抽象的业务执行类 因为通过socket能发送http tcp等 所以写了一个抽象类封装公共的方法
package com.socket.pool; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public abstract class AbstractSocketExecutor { protected SocketMember socketMember=null; protected Socket socket=null; protected String host; protected int port; public AbstractSocketExecutor(SocketMember socketMember){ this.socketMember=socketMember; if(socketMember!=null){ this.socket=socketMember.getSocket(); } host=socket.getInetAddress().getHostName(); port=socket.getPort(); } /** * 发送请求 * * @param request * @param charset * * @Description: TODO */ protected void request(String request,String charset){ OutputStream out=null; try { out=socket.getOutputStream(); out.write(request.getBytes(charset)); } catch (IOException e) { e.printStackTrace(); } } /** * 返回响应stream * * @return * * @Description: TODO */ protected InputStream getResStream(){ InputStream in=null; try { in = socket.getInputStream(); } catch (IOException e) { e.printStackTrace(); } return in; } /** * 设置状态为未使用 * * * @Description: TODO */ public void back(){ //不能关闭流,否则socket会被关闭 /* try { socket.getOutputStream().close(); socket.getInputStream().close(); } catch (IOException e) { e.printStackTrace(); }*/ socketMember.setInUse(false); } }
6.抓取html页面的executor
package com.socket.pool; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * 发送http请求 抓取html页面内容 写的并不好 尤其是判断结束的方式 勉强能用。。。 * @ClassName: HTMLSocketExecutor * @Description: TODO * @Copyright: Copyright (c) 2016 * @Company: 深圳市梦网科技股份有限公司 * @author hxq * @date 2016-8-16 下午02:02:47 * @version V1.0 */ public class HTMLSocketExecutor extends AbstractSocketExecutor { private String charset="UTF-8"; public HTMLSocketExecutor(SocketMember socketMember) { super(socketMember); } public void setRequest(String path){ StringBuilder requestStr=new StringBuilder(); requestStr.append("GET "); requestStr.append(path); requestStr.append(" HTTP/1.1\r\n"); requestStr.append("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36\r\n"); requestStr.append("Host: "); requestStr.append(super.host); if(port!=80){ requestStr.append(":"); requestStr.append(super.port); } requestStr.append("\r\n"); requestStr.append("\r\n"); super.request(requestStr.toString(), charset); } public String getResponse(){ StringBuilder responseResult=new StringBuilder(); BufferedReader socketReader=null; try { socketReader=new BufferedReader(new InputStreamReader(super.getResStream(), charset)) ; String line=null; char[] body=null; int contentLength=-1; boolean isHeadEnd=false; while((line=socketReader.readLine())!=null){ //System.out.println(line); responseResult.append(line); responseResult.append("\r\n"); //检查请求头是否结束 if(!isHeadEnd){ isHeadEnd=isBlankLine(line); if(isHeadEnd){ //立即判断是否为请求头结束 如果是且有content-length 那么立即一次读取body 结束循环 if(contentLength!=-1){ if(contentLength==0){ break; } //如果content-length>0 那么一次性读出响应体 body=new char[contentLength]; int bodyReadEndFlag=socketReader.read(body); responseResult.append(body); if(bodyReadEndFlag==-1){ break; } } } if(contentLength==-1){ //没有赋值 检查content-length并赋值 contentLength=getContentLength(line); } }else{ //已经读过了head结束行 if(isHTMLEnd(line)){ //如果碰到</html> break; } } } } catch (IOException e) { e.printStackTrace(); }finally{ super.back(); } return responseResult.toString(); } private int getContentLength(String line){ int result=-1; if(line.contains("Content-Length")){ int splitIndex=line.indexOf(":"); String length=line.substring(splitIndex+1,line.length()); length=length.trim(); result=Integer.valueOf(length); } return result; } private boolean isBlankLine(String line){ boolean result=false; if("".equals(line)){ result=true; } return result; } private boolean isHTMLEnd(String line){ boolean result=false; if(line.indexOf("</html>")>-1){ result=true; } return result; } }
7.工厂类 用于组装exector对象 实例化executor对象 并将socket对象注入进去 (没有抽象,只能服务HTMLSocketExecutor 。。。 先凑合着用)
package com.socket.pool; public class SocketExcutorFactory { /* private SocketExcutorFactory(){} private final static SocketExcutorFactory instance=new SocketExcutorFactory(); public static SocketExcutorFactory getInstance(){ return instance; }*/ private SocketPool pool=null; public SocketExcutorFactory(String host,int port,boolean daemonFlag){ pool=new SocketPool(host, port); if(daemonFlag){ //守护线程 Thread daemonThread=new Thread(new DaemonThread(pool)); daemonThread.start(); } } public SocketExcutorFactory(String host,int port){ this(host, port, true); } /** * 实例化executor 并从池中拿出一个socket注进去 * * @return * * @Description: TODO */ public HTMLSocketExecutor getHttpExecutor(){ HTMLSocketExecutor executor=null; executor=new HTMLSocketExecutor(pool.getMemberFromPool()); return executor; } }
单线程测试类
package com.socket.pool; public class TestPool { public static void main(String[] args) { String host="www.cnblogs.com"; int port=80; SocketExcutorFactory factory=new SocketExcutorFactory(host, port,false); HTMLSocketExecutor executor=factory.getHttpExecutor(); executor.setRequest("/zhangweizhong/p/5772330.html"); String res=executor.getResponse(); System.out.println(res); } }
多线程测试
package com.socket.pool; public class ExcutorThread implements Runnable { HTMLSocketExecutor excutor=null; private static SocketExcutorFactory factory=new SocketExcutorFactory("www.baidu.com", 80); public ExcutorThread(){ excutor=factory.getHttpExecutor(); } public void run() { excutor.setRequest("/"); String res=excutor.getResponse(); if(res.contains("302 Found")){ System.out.println(Thread.currentThread().getName()+" success"); }else{ System.out.println(Thread.currentThread().getName()+" fail"); } } public static void main(String[] args) { Thread thread=null; for(int i=0;i<10;i++){ thread=new Thread(new ExcutorThread()); thread.start(); } } }
源码: