zoukankan      html  css  js  c++  java
  • 计算机网络--http代理server的设计与实现

    一、Socket编程的client和服务端的主要步骤:

    Java Socket编程:对于http传输协议

    client:

    1、创建新的socket,绑定serverhost和port号

    2Socket创建成功后获得对应的输出流

    3、将请求报文通过输出流传到server,记得flush()刷新缓存

    4、创建该socket所相应的输入流,获取server的相应报文

    服务端:

    1、通过建立对应port的socket实现监听某port的socket请求

    2、当有别的socket请求连接就開始监听socket的信息,接收到请求报文

    3、依据对请求报文的解析,得到请求者的url、port还有请求信息

    4、将响应信息还有必要的头部连接形成响应报文。通过socket的输出流返回给请求client

    二、HTTP代理server的基本原理:

    代理server,即作为真实server的一个代理端。client的请求信息不是发送的真实请求的server而是发送的代理server,此时代理server是作为一个server。之后代理server通过解析client的请求信息,再向真实server发送请求报文,获得请求的信息。此时代理server是作为一个client。

    使用代理server的优点是:

    1、在请求client和真实server之间加入了一层,这样就可控的对于请求的响应报文做一些限制或者是改变。比如站点过滤、钓鱼站点等使得响应到client的信息是代理server处理过的。

    2、还有就是请求报文先发送到代理server。这样代理server能够设立缓存,通过对请求报文解析后代理server能够通过查找本地缓存。假设有缓存好的,而且通过向server发送是否更新的信息后得到没有改动后就能够直接从代理server将响应报文返回给client,这样降低了服务端的负载,降低了流量

    三、HTTP代理server的程序流程图:

     

    中间代理server能够设定对请求报文和响应报文做一些改动

    四、实现HTTP代理server的关键技术及解决方式

    1、关键技术:socket编程发送和接受报文

     因为http的请求和响应报文都有特定的格式,所以一旦对于报文的格式理解错误就不能获得正确的响应,比如:对于请求报文每一行须要换行符,可是在编程的时候须要清楚理解换行符和回车符,假设在写请求报文时单单以 作为换行组成的报文将得不到server的响应会产生400 bad request错误。

    解决方式:每一行换行须要以回车符和换行符即    两个一起。这样才干得到正确的报文。在读取响应报文时也要注意会有两个符号作为一行的换行,所以在读取到 时就表明一行已经读取完成。并且下一行之前另一个 须要清除

    2、关键技术:对于client、代理server、真正server之间的响应线程之间的正确顺序的组织;

    解决方式:使用线程组织各部分之间的调度关系。代理server处于一直监听状态。当和client交互时处于server的角色,当和server交互时处于client的角色。

    3、关键技术:对于请求报文信息的解析,包含正式请求的server的url、port号、host等信息的正确获取

    解决方式:按行提取信息,使用字符串处理函数提取实用的信息

    4、关键技术:使用缓存的代理server,须要做到保存请求报文对应的响应报文。顺序不能有差错并且信息不能有缺漏

    解决方式:使用日志,在每接受一次请求的时候。将请求的完整url保存到日志中,之后一旦得到对应信息直接保存在url下方,每次通过匹配url得知其下方的响应是否是所需的。这样方便查找和改动

    五、HTTP代理server的实验验证过程以及实验结果

    1、基本功能:代理上网

     



    2、扩展功能:屏蔽站点

     

    3、扩展功能:钓鱼站点

    选择搜狗可是进入的是淘宝网

     

    4、扩展功能:带有缓存处理

    每一次都会在日志中找是否已有对应的缓存,已有信息则向server发送时间确认报文,后决定是否使用缓存中信息

     

    六、HTTP代理server源码(带有具体凝视)

    package test;
    
    import java.io.*;
    import java.net.*;
    import java.util.*;
    
    public class MyHttpProxy extends Thread {
    	public static int CONNECT_RETRIES = 5; // 尝试与目标主机连接次数
    	public static int CONNECT_PAUSE = 5; // 每次建立连接的间隔时间
    	public static int TIMEOUT = 8000; // 每次尝试连接的最大时间
    	public static int BUFSIZ = 1024; // 缓冲区最大字节数
    	public static boolean logging = false; // 是否记录日志
    	public static OutputStream log_S = null; // 日志输出流
    	public static OutputStream log_C = null; // 日志输出流
    	public static OutputStream log_D = null; // 响应报文日志
    	public static int count = -1;
    	public static List<String> requestInfo = new ArrayList<String>();
    	public static List<String> cacheInfo;
    	Socket ssocket = null;
    	// cis为client输入流。sis为目标主机输入流
    	InputStream cis = null, sis = null;
    	BufferedReader cbr = null, sbr = null; // 转化为字符流读取便于比較
    	// cos为client输出流。sos为目标主机输出流
    	OutputStream cos = null, sos = null;
    	PrintWriter cpw = null, spw = null;// 转化为字符流
    	String buffer = ""; // 读取请求头
    	String URL = ""; // 读取请求URL
    	String host = ""; // 读取目标主机host
    	int port = 80; // 默认端口80
    	String findUrl = "";//在缓存中查找的url
    	// 与client相连的Socket
    	protected Socket csocket;
    
    	public MyHttpProxy(Socket cs) {
    		try {
    			csocket = cs;
    			cis = csocket.getInputStream(); // 代理server作为server接受client的请求
    			cbr = new BufferedReader(new InputStreamReader(cis));
    			cos = csocket.getOutputStream(); // 代理server作为server向client发出响应
    			cpw = new PrintWriter(cos);
    			start();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	public void writeLog(int c, int browser) throws IOException {
    		if (browser == 1)
    			log_C.write((char) c);
    		else if (browser == 2)
    			log_S.write((char) c);
    		else
    			log_D.write((char) c);
    	}
    
    	public void writeLog(byte[] bytes, int offset, int len, int browser)
    			throws IOException {
    		for (int i = 0; i < len; i++)
    			writeLog((int) bytes[offset + i], browser);
    	}
    
    	public void run() {  
    			try { 
    				csocket.setSoTimeout(TIMEOUT);
    				System.out.println("到了读取第一行");
    				buffer = cbr.readLine(); // 获取首部行
    				System.out.println("buffer:" + buffer);
    
    				URL = getRequestURL(buffer);
    				System.out.println(URL);
    				if(URL.equals("http://www.sogou.com/")){
    					URL = "http://www.taobao.com/";
    					buffer = "GET "+URL+" HTTP/1.1"; 
    					requestInfo.add("Accept: text/html, application/xhtml+xml, */*"); 
    					requestInfo.add("Accept-Language: zh-Hans-CN,zh-Hans;q=0.8,en-US;q=0.5,en;q=0.3"); 
    					requestInfo.add("User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.2; WOW64; Trident/6.0)");
    					requestInfo.add("Accept-Encoding: gzip, deflate");
    					requestInfo.add("Proxy-Connection: Keep-Alive");
    					requestInfo.add("DNT: 1");
    					requestInfo.add("Host: www.taobao.com");
    					requestInfo.add("Cookie: thw=cn; isg=0BC4B5EFD7C7FCFEB73317770EA7F3F5; l=AeVoHE44ZTsle7DjpW8fBSV7pbSl-2U7; cna=GCHeDZQAVwkCAdvZ9Apwg8rH; t=1a1386bec550ab78d1aaf5ad5b90e044; mt=ci%3D-1_0; _med=dw:1366&dh:768&pw:1366&ph:768&ist:0");
    				}
    				else if(URL.equals("http://www.qq.com/")) {
    					URL = "";
    				}
    				int n;
    				// 抽取host
    				n = URL.indexOf("//");
    				if (n != -1)
    					host = URL.substring(n + 2); // www.baidu.com/
    				n = host.indexOf('/');
    				if (n != -1)
    					host = host.substring(0, n);// www.baidu.com
    				n = URL.indexOf('?

    '); if(n != -1) findUrl = URL.substring(0,n); else findUrl = URL; // 分析可能存在的端口号 n = host.indexOf(':'); if (n != -1) { port = Integer.parseInt(host.substring(n + 1)); host = host.substring(0, n); } int retry = CONNECT_RETRIES; while (retry-- != 0 && !host.equals("")) { try { System.out.println("端口号:" + port + "主机:" + host); System.out.println("第一行是 " + retry + ":" + buffer); ssocket = new Socket(host, port); // 尝试建立与目标主机的连接 break; } catch (Exception e) { e.printStackTrace(); } // 等待 Thread.sleep(CONNECT_PAUSE); } if (ssocket != null) { ssocket.setSoTimeout(TIMEOUT); sis = ssocket.getInputStream(); // 代理server作为client接受响应 sbr = new BufferedReader(new InputStreamReader(sis)); sos = ssocket.getOutputStream(); // 代理server作为client发出请求 spw = new PrintWriter(sos); String modifTime = findCache(findUrl);// 在缓存中寻找是否之前已经缓存过这个url的信息 System.out.println("上一次改动的时间为:" + modifTime);// writeLog(buffer.getBytes(), 0, buffer.length(), 1); writeLog(buffer.getBytes(), 0, buffer.length(), 3); writeLog(" ".getBytes(), 0, 2, 3); // 之前没有缓存 if (modifTime == null) { while (!buffer.equals("")) { buffer += " "; if(buffer.contains("www.taobao.com")) { //屏蔽人人网,假设是淘宝就发送淘宝的报文 int k = 0; while(requestInfo.size() - k > 0) { spw.write(buffer); buffer = requestInfo.get(k++); buffer += " "; } break; } else{ spw.write(buffer); writeLog(buffer.getBytes(), 0, buffer.length(), 1); System.out.print("向server发送请求:"+buffer); buffer = cbr.readLine(); } } spw.write(" "); writeLog(" ".getBytes(), 0, 2, 1); spw.flush(); // 读取server的响应信息 int length; byte bytes[] = new byte[BUFSIZ]; while (true) { try { if ((length = sis.read(bytes)) > 0) { // 读取client的请求转给server cos.write(bytes, 0, length); if (logging) { writeLog(bytes, 0, length, 1); writeLog(bytes,0,length,3); } } else if (length < 0) break; } catch (SocketTimeoutException e) { } catch (InterruptedIOException e) { System.out.println(" Request Exception:"); e.printStackTrace(); } } if(count == 0) { System.out.println(cbr.readLine()); } cpw.write(" "); writeLog(" ".getBytes(), 0, 2, 3); writeLog(" ".getBytes(), 0, 2, 2); cpw.flush(); } else { buffer += " "; spw.write(buffer); System.out.print("向server发送确认改动时间请求:"+buffer); String str1 = "Host: " + host + " "; spw.write(str1); String str = "If-modified-since: " + modifTime + " "; spw.write(str); spw.write(" "); spw.flush(); System.out.print(str1); System.out.print(str); String info = sbr.readLine(); System.out.println("server发回的信息是:"+info); if (info.contains("Not Modified")) { int j = 0; System.out.println("使用缓存中的数据"); while (j < cacheInfo.size()) { info = cacheInfo.get(j++); info += " "; System.out.print(info); cpw.write(info); } cpw.write(" "); cpw.flush(); } else { System.out.println("有更新,使用新的数据"); while (!info.equals("")) { info += " "; System.out.print("新的数据是:" + info); cpw.write(info); info = sbr.readLine(); } cpw.write(" "); cpw.flush(); } } } } catch (Exception e) { e.printStackTrace(); } } public String getRequestURL(String buffer) { String[] tokens = buffer.split(" "); String URL = ""; if (tokens[0].equals("GET")) for (int index = 0; index < tokens.length; index++) { if (tokens[index].startsWith("http://")) { URL = tokens[index]; break; } } return URL; } public void pipe(InputStream cis, InputStream sis, OutputStream sos, OutputStream cos) { try { int length; byte bytes[] = new byte[BUFSIZ]; while (true) { try { if ((length = cis.read(bytes)) > 0) { // 读取client的请求转给server sos.write(bytes, 0, length); if (logging) writeLog(bytes, 0, length, 1); } else if (length < 0) break; } catch (SocketTimeoutException e) { } catch (InterruptedIOException e) { System.out.println(" Request Exception:"); e.printStackTrace(); } try { if ((length = sis.read(bytes)) > 0) {// 接受server的响应回传给请求的client cos.write(bytes, 0, length); // 由于是按字节读取,所以将回车和换行符也传递过去了 if (logging) { writeLog(bytes, 0, length, 1); writeLog(bytes, 0, length, 3); } } } catch (SocketTimeoutException e) { } catch (InterruptedIOException e) { System.out.println(" Response Exception:"); e.printStackTrace(); } } } catch (Exception e0) { System.out.println("Pipe异常: " + e0); } } public static void startProxy(int port, Class clobj) { try { ServerSocket ssock = new ServerSocket(port); while (true) { Class[] sarg = new Class[1]; Object[] arg = new Object[1]; sarg[0] = Socket.class; try { java.lang.reflect.Constructor cons = clobj .getDeclaredConstructor(sarg); arg[0] = ssock.accept(); System.out.println("启动线程:"+count++); cons.newInstance(arg); // 创建HttpProxy或其派生类的实例 } catch (Exception e) { Socket esock = (Socket) arg[0]; try { esock.close(); } catch (Exception ec) { } } } } catch (IOException e) { System.out.println(" StartProxy Exception:"); e.printStackTrace(); } } // 測试用的简单main方法 static public void main(String args[]) throws FileNotFoundException { System.out.println("在端口8888启动代理server "); OutputStream file_S = new FileOutputStream(new File("log_s.txt")); OutputStream file_C = new FileOutputStream(new File("log_c.txt")); OutputStream file_D = new FileOutputStream("log_d.txt",true); MyHttpProxy.log_S = file_S; MyHttpProxy.log_C = file_C; MyHttpProxy.log_D = file_D; // 直接存储相关URl相应的响应报文 MyHttpProxy.logging = true; MyHttpProxy.startProxy(8888, MyHttpProxy.class); } public String findCache(String head) { cacheInfo = new ArrayList<String>(); String resul = null; int count = 0; try { // 直接在存有url和相应信息的文件里查找 InputStream file_D = new FileInputStream("log_d.txt"); String info = ""; while (true) { int c = file_D.read(); if (c == -1) break; // -1为结尾标志 if (c == ' ') { file_D.read(); break;// 读入每一行数据 } if (c == ' ') break; info = info + (char) c; } System.out.println("第一次得到:" + info); System.out.println("要找的是:" + head); int m = 0; while ((m = file_D.read()) != -1 && info!=null) { //System.out.println("在寻找:"+info); // 找到同样的,那么它以下的就是响应信息。找上次改动的时间 if (info.contains(head)) { String info1; do { System.out.println("找到同样的了:" + info); info1 = ""; if(m!=' ' && m != ' ') info1 += (char) m; while (true) { m = file_D.read(); if (m == -1) break; if (m == ' ') { file_D.read(); break; } if (m == ' ') { break; } info1 += (char) m; } System.out.println("info1是:"+info1); if (info1.contains("Last-Modified:")) { resul = info1.substring(16); } cacheInfo.add(info1); if(info1.equals("")){ System.out.print("我是空"); return resul; } } while (!info1.equals("") && info1 != null && m != -1); } info = ""; while (true) { if (m == -1) break; if (m == ' ') { file_D.read(); break; } if (m == ' ') break; info += (char) m; m = file_D.read(); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return resul; } }



  • 相关阅读:
    html5 本地存储
    javascript 中的 this 关键字详解
    重绘和回流
    javascript 的预解释机制
    zepto.js 实现原理解析
    python3.6新特性
    python面试终极准备
    科大讯飞语音合成api
    智能儿童玩具
    MySQL与MongoDB
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5203624.html
Copyright © 2011-2022 走看看