计算机网络--http代理server的设计与实现
一、Socket编程的client和服务端的主要步骤:
Java Socket编程:对于http传输协议
client:
1、创建新的socket,绑定serverhost和port号
2、Socket创建成功后获得对应的输出流
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的请求和响应报文都有特定的格式,所以一旦对于报文的格式理解错误就不能获得正确的响应,比如:对于请求报文每一行须要换行符,可是在编程的时候须要清楚理解换行符和回车符,假设在写请求报文时单单以\n作为换行组成的报文将得不到server的响应会产生400 bad request错误。
解决方式:每一行换行须要以回车符和换行符即\r \n 两个一起。这样才干得到正确的报文。在读取响应报文时也要注意会有两个符号作为一行的换行,所以在读取到\r时就表明一行已经读取完成。并且下一行之前另一个\n须要清除
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("\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("向server发送请求:"+buffer);
buffer = cbr.readLine();
}
}
spw.write("\r\n");
writeLog("\r\n".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("\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("向server发送确认改动时间请求:"+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("server发回的信息是:"+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) { // 读取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("\nRequest 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("\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启动代理server\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;
} }
计算机网络--http代理server的设计与实现的更多相关文章
- 反向代理:是指以代理server来接收Internet上的请求,然后将请求转发到内部网络的server上,并将结果返回给Internet上连接的client,此时的代理server对外就表现为反向代理server。
Nginx安装好之后.開始使用它来简单实现反向代理与负载均衡的功能.在这之前.首先得脑补一下什么是反向代理和负载均衡. 反向代理:是指以代理server来接收Internet上的请求,然后将 ...
- JAVA如何设置代理server,s取消代理erver
1.一个简短的引论 代理server(Proxy Server)是一种重要的server安全功能,它的工作主要在开放系统互联(OSI)模型的会话层,从而起到防火墙的作用. 代理server大多被用来连 ...
- Java通过代理server上网
完整代码 package com.proj.net; //导入编码的jar文件 import it.sauronsoftware.base64.Base64; import java.io.Buffe ...
- windows平台HTTP代理server搭建(CCproxy)
HTTP代理(CCproxy) 一.拓扑图 二.CCproxy的安装和配置 1.安装CCproxy (1)下载CCproxy无线破解版(没破解的都仅仅支持最多三个用户同一时候连接). (2)按说明安装 ...
- 国外代理server
这里有几个国外的代理server 另外在网上能够找到很多这种 不能用的时候就在网上搜搜 稳定代理server 有非常多的 IP port 显示地址 24.245.58.130:32167 美国 新泽西 ...
- [C++] 获取IE代理server的账号password
非常多程序须要使用'浏览器设置'的代理server,IE设置的代理server有可能是须要账号password的.如何编程获取浏览器设置的代理server的账号password呢? InternetQ ...
- 【学习记录】第一章 数据库设计-《SQL Server数据库设计和开发基础篇视频课程》
一.课程笔记 1.1 软件开发周期 (1)需求分析阶段 分析客户的业务和数据处理需求. (2)概要设计阶段 设计数据库的E-R模型图,确认需求信息的正确和完整. /* E-R图:实体-关系图(Ent ...
- Android SDK代理server解决国内不能更新下载问题
读者须知:本篇文章中最靠谱的是第三种方式,近期有读者反映第三种方式也不行了,以下提供一点其它途径的开源镜像网站: 国内高校的开源镜像站 中国科学技术大学(debian.ustc.edu.cn) 上海交 ...
- 《SQL Server 2000设计与T-SQL编程》
<SQL Server 2000设计与T-SQL编程> <SQL Server 2000设计与T-SQL编程>笔记1 http://dukedingding.blog.sohu ...
随机推荐
- 简洁的jsp
在开发 是使用tomcat7版本(7的jslt表达式语法检查更加严格) 1.去除生产html的不必要的空行 <%@ page trimDirectiveWhitespaces="tru ...
- [工具]Mac下非常好用的快捷终端Dterm
[工具]Mac下非常好用的快捷终端Dterm A command line anywhere and everywhere 这是可在任何目录下直接用全局快捷键直接调出命令输入框的小工具,非常好用 作 ...
- 利用margin代替小图标的绝对定位;使代码更简洁
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 关于关注和取消关注的nodejs写法
本例子的关注和取消关注,是通过ajax的方法实现的:nodejs后台写好api接口:响应前台的ajax 先看ajax的代码实现: // 用户关注标签 function subscribe(uid, t ...
- AU3学习笔记
目录 1. AU3是什么?能做什么? 2. 乱学AU3中的命令(语言相关)? 3. 通过简单示例学习AU3? 4. 正则表达式的学习(对大小写敏感) 5.对于GUI的相关学习 1. AU ...
- 基于Linux 3.0.8 Samsung FIMC(S5PV210) 的摄像头驱动框架解读(一)
作者:咕唧咕唧liukun321 来自:http://blog.csdn.net/liukun321 FIMC这个名字应该是从S5PC1x0開始出现的.在s5pv210里面的定义是摄像头接口.可是它相 ...
- html系列教程--文本格式化
<b>定义粗体文本</b> <br /> <strong>定义加重语气</strong> <br /> <big>定 ...
- html系列教程--标题,水平线,注释以及段落
HTML标题 标题,用来显示文章重要性的文字,包含了文章的主旨,类似于作文题目. 标题(Heading)是通过 <h1> - <h6> 等标签进行定义的,由大到小一次排列,h1 ...
- <原>ASP.NET 学习笔记之HTML helper中参数何时会是路由参数,何时又会是query string?
HTML helper中参数何时会是路由参数,何时又会是query string? @Html.ActionLink("Edit", "Edit", new ...
- SQL Server数据库--》top关键字,order by排序,distinct去除重复记录,sql聚合函数,模糊查询,通配符,空值处理。。。。
top关键字:写在select后面 字段的前面 比如你要显示查询的前5条记录,如下所示: select top 5 * from Student 一般情况下,top是和order by连用的 orde ...