计算机网络--http代理服务器的设计与实现

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

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

客户端:

1、创建新的socket,绑定服务器host和端口号

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

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

4、创建该socket所对应的输入流,获取服务器的相应报文

服务端:

1、通过建立相应端口的socket实现监听某端口的socket请求

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

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

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

二、HTTP代理服务器的基本原理:

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

使用代理服务器的好处是:

1、在请求客户端和真实服务器之间添加了一层,这样就可控的对于请求的响应报文做一些限制或者是改变,例如网站过滤、钓鱼网站等使得响应到客户端的信息是代理服务器处理过的;

2、还有就是请求报文先发送到代理服务器,这样代理服务器可以设立缓存,通过对请求报文解析后代理服务器可以通过查找本地缓存,如果有缓存好的,并且通过向服务器发送是否更新的信息后得到没有修改后就可以直接从代理服务器将响应报文返回给客户端,这样减少了服务端的负载,减少了流量。

三、HTTP代理服务器的程序流程图:

中间代理服务器可以设定对请求报文和响应报文做一些修改

四、实现HTTP代理服务器的关键技术及解决方案

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

由于http的请求和响应报文都有特定的格式,所以一旦对于报文的格式理解错误就不能获得正确的响应,例如:对于请求报文每一行需要换行符,但是在编程的时候需要清楚理解换行符和回车符,如果在写请求报文时单单以\n作为换行组成的报文将得不到服务器的响应会产生400 bad request错误。

解决方案:每一行换行需要以回车符和换行符即\r \n 两个一起,这样才能得到正确的报文。在读取响应报文时也要注意会有两个符号作为一行的换行,所以在读取到\r时就表明一行已经读取完毕,而且下一行之前还有一个\n需要清除

2、关键技术:对于客户端、代理服务器、真正服务器之间的响应线程之间的正确顺序的组织;

解决方案:使用线程组织各部分之间的调度关系,代理服务器处于一直监听状态,当和客户端交互时处于服务器的角色,当和服务器交互时处于客户端的角色。

3、关键技术:对于请求报文信息的解析,包括正式请求的服务器的url、端口号、host等信息的正确获取

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

4、关键技术:使用缓存的代理服务器,需要做到保存请求报文相应的响应报文,顺序不能有差错而且信息不能有缺漏

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

五、HTTP代理服务器的实验验证过程以及实验结果

1、基本功能:代理上网

2、扩展功能:屏蔽网站

3、扩展功能:钓鱼网站

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

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

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

六、HTTP代理服务器源代码(带有详细注释)

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为客户端输入流,sis为目标主机输入流
	InputStream cis = null, sis = null;
	BufferedReader cbr = null, sbr = null; // 转化为字符流读取便于比较
	// cos为客户端输出流,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
	// 与客户端相连的Socket
	protected Socket csocket;

	public MyHttpProxy(Socket cs) {
		try {
			csocket = cs;
			cis = csocket.getInputStream(); // 代理服务器作为服务器接受客户端的请求
			cbr = new BufferedReader(new InputStreamReader(cis));
			cos = csocket.getOutputStream(); // 代理服务器作为服务器向客户端发出响应
			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(); // 代理服务器作为客户端接受响应
					sbr = new BufferedReader(new InputStreamReader(sis));
					sos = ssocket.getOutputStream(); // 代理服务器作为客户端发出请求
					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("\r\n".getBytes(), 0, 2, 3);
					// 之前没有缓存
					if (modifTime == null) {
						while (!buffer.equals("")) {
							buffer += "\r\n";
							if(buffer.contains("www.taobao.com")) { //屏蔽人人网,如果是淘宝就发送淘宝的报文
								int k = 0;
								while(requestInfo.size() - k > 0) {
									spw.write(buffer);
									buffer = requestInfo.get(k++);
									buffer += "\r\n";
								}
								break;
							}
							else{
								spw.write(buffer);
							writeLog(buffer.getBytes(), 0, buffer.length(), 1);
							System.out.print("向服务器发送请求:"+buffer);
							buffer = cbr.readLine();
							}
						}
						spw.write("\r\n");
						writeLog("\r\n".getBytes(), 0, 2, 1);
						spw.flush();
						// 读取服务器的响应信息
						int length;
						byte bytes[] = new byte[BUFSIZ];
						while (true) {
							try {
								if ((length = sis.read(bytes)) > 0) { // 读取客户端的请求转给服务器
									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("\nRequest Exception:");
								e.printStackTrace();
							}
						}
						if(count == 0) {
							System.out.println(cbr.readLine());
						}
						cpw.write("\r\n");
						writeLog("\r\n".getBytes(), 0, 2, 3);
						writeLog("\r\n".getBytes(), 0, 2, 2);
						cpw.flush();
					} else {
						buffer += "\r\n";
						spw.write(buffer);
						System.out.print("向服务器发送确认修改时间请求:"+buffer);
						String str1 = "Host: " + host + "\r\n";
						spw.write(str1);
						String str = "If-modified-since: " + modifTime
								+ "\r\n";
						spw.write(str);
						spw.write("\r\n");
						spw.flush();
						System.out.print(str1);
						System.out.print(str);

						String info = sbr.readLine();
						System.out.println("服务器发回的信息是:"+info);
						if (info.contains("Not Modified")) {
							int j = 0;
							System.out.println("使用缓存中的数据");
							while (j < cacheInfo.size()) {
								info = cacheInfo.get(j++);
								info += "\r\n";
								System.out.print(info);
								cpw.write(info);
							}
							cpw.write("\r\n");
							cpw.flush();
						} else {
							System.out.println("有更新,使用新的数据");
							while (!info.equals("")) {
								info += "\r\n";
								System.out.print("新的数据是:" + info);
								cpw.write(info);
								info = sbr.readLine();
							}
							cpw.write("\r\n");
							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) { // 读取客户端的请求转给服务器
						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("\nRequest Exception:");
					e.printStackTrace();
				}
				try {
					if ((length = sis.read(bytes)) > 0) {// 接受服务器的响应回传给请求的客户端
						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("\nResponse 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("\nStartProxy Exception:");
			e.printStackTrace();
		}
	}

	// 测试用的简单main方法
	static public void main(String args[]) throws FileNotFoundException {
		System.out.println("在端口8888启动代理服务器\n");
		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 == '\r') {
					file_D.read();
					break;// 读入每一行数据
				}
				if (c == '\n')
					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!='\r' && m != '\n')
							info1 += (char) m;
						while (true) {
							m = file_D.read();
							if (m == -1)
								break;
							if (m == '\r') {
								file_D.read();
								break;
							}
							if (m == '\n') {
								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 == '\r') {
						file_D.read();
						break;
					}
					if (m == '\n')
						break;
					info += (char) m;
					m = file_D.read();
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return resul;
	}

}
时间: 2024-12-31 06:27:47

计算机网络--http代理服务器的设计与实现的相关文章

《Docker技术入门与实战》pdf

下载地址:网盘下载 内容简介  · · · · · · [编辑推荐] <Docker技术入门与实战>是中国首部docker著作,一线Docker先驱实战经验结晶,来自IBM和新浪等多位技术专家联袂推荐! <Docker技术入门与实战>结合企业生产环境,深入浅出地剖析 Docker 的核心概念.应用技巧.实现原理以及生态环境,为解决各类问题提供了有价值的参考. [内容简介] 在云计算时代,开发者将应用转移到云上已经解决了硬件管理的问题,然而软件配置和管理相关的问题依然存在.Docke

落地微服务之前,先想想你的团队结构是否合理!

2016-10-18 王昕 译 聊聊架构 有关微服务的技术Docker火热起来之后也进一步升温.一般来说,IT业界,尤其是工程师们更加关心国外专家的技术文章.但事实上,对于软件项目成功与否来说,往往是企业中人的因素决定结果,企业文化因素决定结果,对于微服务架构,则更是如此:因为微服务的分割是从企业业务开始,而不是从软件技术开始.本文由轻元科技首席架构师王昕翻译,如有疑问,欢迎留言讨论. 似乎每过五到十年,在软件行业,特别是企业集成或者是企业应用领域,就会出现一些新的方法论或架构方式,声称能够让你

4-2-word2003-Word文本操作

二.Word文本操作 1.文本清除 1.1.明白下面两个键的不同 Backspace(退格键) 删除光标以左的内容 Delete (删除键)    删除光标以右的内容 1.2.用鼠标拖选的方法,把下面两段一次性删除 拖选 开始位置单击+shift+结束位置单击     Internet源于美国,最初为了实现科研和军事部们里不同结构的计算机网络之间互联而设计,现已普及到全世界.随着通信线路的不断改进,特别是微机的普及,Internet几乎无所不在,无所不为了.    我国于1994年4月正式连入I

2015秋课程

  一 二 三 四 五 一   操作系统    操作系统   二   计算机网络  软件测试方法 计算机网络   三 算法分析与设计   计算机体系结构     四 算法分析与设计   计算机体系结构    

第一周大作业1

---恢复内容开始--- 田继平-软件工程-第一次作业 一.自我介绍大家好,我叫田继平,目前就读于东北师范大学信息科学与技术学院计算机技术专业,是一名在读的专硕研一学生,本科就读于北华大学计算机科学技术学院,专业是计算机科学与技术,籍贯吉林省榆树市.二.回答作业问题1.回想一下你曾经对计算机专业的畅想我高考后报考的是计算机科学与技术,当时对计算机技术基本了解为零,当时以为什么东西都会用到计算机,学计算机以后不会找不到工作,刚开学的时候对计算机一窍不通,甚至连优盘拔出来,要单击右键然后弹出都不会,

PSP总结报告

回答作业问题 1.回想一下你曾经对计算机专业的畅想 我高考后报考的是计算机科学与技术,当时对计算机技术基本了解为零,当时以为什么东西都会用到计算机,学计算机以后不会找不到工作,刚开学的时候对计算机一窍不通,甚至连优盘拔出来,要单击右键然后弹出都不会,由于自己是乡下来的基本上没接触过计算机,开学后老师教了计算机的基础知识,然后教了各种语言,才算大概了解了计算机技术,经过一个学期,我认为我选择计算机专业是多么明智的选择.我觉得我接触的课程符合我对计算机专业的期待,经过一个学期,我突然特别喜欢专业知识

综合布线系统在政府行业有什么作用

随着电脑和计算机网络的普及,政府行业的信息传输已经到了无纸化办公的阶段,大量的本地信息.外地信息.全球信息都会在办公室电脑的显示屏上和电话听筒中. 在大多数政府机关中都已经采用了内网物理隔离的方式,并且开始考虑到电子显示和电子档案需求,要求装有消防控制设备.公共广播设备和防盗报警设备.电子考勤.电视监控设备. 在政府行业中,综合布线系统的作用大致包含: 分布广泛 作为承载计算机网络和电话网络的综合布线系统,为了满足应用上的要求,它们已经遍布政府行业的各种建筑物内外,敷设到了建筑物内的各个角落,在

20971多处理机操作系统

多处理机操作系统 10.1  多处理机系统的基本概念 10.1.1  多处理机系统的引入 进入70年代后,已采用多处理机的系统结构从提高运行速度方面来增强系统性能.实际上,多处理机系统MPS就是采用并行技术,令多个单CPU同时运行,使总体的计算能力比单CPU计算机系统的强大得多. 1.  CPU的时钟频率问题 在早期,人们首先是采用提高CPU时钟频率的方法提高计算速度.CPU的时钟频率已从早期的每秒钟嘀嗒数十次,发展到现在的数兆赫兹(GHz),这主要得益于芯片制造工艺水平的提高. 2. 增加系统

计算机网络- 可靠数据传输协议-停等协议的设计与实现

一.所实现停等协议简介 我设计的程序实现的是rdt3.0版本的停等协议,发送端将数据包以0.1交替的顺序发送数据包,当发送0数据包之后开始计时,只有接收到ack0才能继续发送1数据包,如果在没有接收到ack0的时候已经超时,这时候需要重传数据包0: 接收方按照所收到的数据包的编号返回相应的ack,当上一个收到的是数据包0后下一个只有是数据包1才能接收放到接收文件中,如果下一个还是数据包0那么就要丢弃. 二.分组格式 数据报格式: 三.UDP编程的特点 UDP协议是无连接的数据传输格式,所以就不用