文档章节

基于JDK http包实现的简单代理服务器

zhangwenwen
 zhangwenwen
发布于 2015/12/04 16:33
字数 1455
阅读 450
收藏 11

利用Java 自带的Http Api 实现的一个简易的代理服务器,实现对常用的在服务器上的静态内容进行缓存,减少网络带宽的浪费。

一、原理

本次实现的代理缓存服务器的主要的功能是当客户端访问相关的网页的时候,首先检测本地是否存在相关的数据,若存在,则直接发送头部具有IF-Modified-Then的http请求,检测数据本地数据是否是最新的,若不是最新的,则对本地数据进行更新并向客户端返回相关数据;若不存在,则向主机请求相关的数据,存储在本地并向客户端返回数据。详细的流程见图1:

图 1 代理缓存服务器的工作流程

二、具体步骤

代理服务器的创建及firefox代理的配置

代理服务器监听的端口为本地的9999端口,运行起来后就可以将firefox设置代理进行访问。

建立代理服务器的设置代码如下:

public static void main(String[] args) {
		// TODO
		HttpServer server = null;
		try {
			MyServerInit();
			System.out.println("[INFO]创建服务器.....");
			server = HttpServer.create(new InetSocketAddress(9999), 0);
			System.out.println("[INFO]设置服务器的处理程序代码....");
			server.createContext("/", new MyProxyServerHandler());
			server.setExecutor(null);
			System.out.println("[INFO]服务器启动中....成功!");
			server.start();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

Firefox的设置如下图2:

图2 火狐浏览器的代理配置

MyServerInit的实现如下:

private static void MyServerInit() {
		// TODO Auto-generated method stub
		File f = new File(ServerConf.ERROR_HTML);
		if (!f.exists()) {
			f.getParentFile().mkdirs();
			try {
				f.createNewFile();
				System.out.println("[INFO初始化服务器....]");
				System.out.println("[INFO]创建全局错误文件html页面...");
				// 文件中写入错误的信息数据
				BufferedWriter bufferedWriter = new BufferedWriter(
						new FileWriter(f));
				String content = "<font color='#ff0000'>请求发生错误,请仔细URL是否正确或者是代理服务器网络发生故障,<br>了解更多可以联系管理员</font>";
				bufferedWriter.write(content);
				bufferedWriter.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}

在上述代码中,class MyProxyServerHandler implements HttpHandler主要用来对请求进行处理,下面对其进行详细的说明。

三、MyProxyServerHandler逻辑处理

 1,接收来自客户端的数据请求的主机以及uri,生成返回的html页面的本地路径,代码如下:

// 得到请求的头部
Headers headers = httpExchange.getRequestHeaders();
// 得到请求主机的名称
String host = headers.get("Host").get(0);
// 得到请求的uri
URI uri = httpExchange.getRequestURI();
String html_file = ServerConf.DOCUMENT_DIR + host + "/"
	+ DigestUtils.md5Hex(ContentUtils.parseFile(uri.toURL().getFile()));

在上述的代码中,将请求的文件的路径进行MD5处理是为了防止有的文件路径名过长,抛出异常,所用的工具包是apache的commons-codec-1.10.

2,检测请求的主机的文件夹是否存在本地,若不存在则创建,若存在,则继续检测请求的文件是否存在本地,相关的代码如下:

public boolean IsHostExist(String host) {
		File host_dir = new File(root + host);
		if (host_dir.exists() && host_dir.isDirectory()) {
			return true;
		} else {
			if (host_dir.mkdirs()) {
			System.out.println("主机文件:" + host + "创建成功!");
			} else {
			System.err.println("主机文件:" + host + "创建失败!");
			}
			return false;
		}
	}

3,检测所请求的本地文件是否存在,若存在,则发送含有IF-Modified-Then的http请求,若不存在则直接发送http请求并接收得到的数据进行分析处理;代码如下:

// 检查请求的文件是否存在
if (responseHtml.exists()) {
// 发送http的head操作,得到文档的状态信息
	String timestamp = new Date(responseHtml.lastModified())
				.toGMTString();
	HttpGet getCheck = new HttpGet(uri.toURL().toString());
	getCheck.addHeader("If-Modified-Since", timestamp);
	CloseableHttpResponse responseCheck = client.execute(getCheck);
	// 得到返回的code
	int respcode = responseCheck.getStatusLine().getStatusCode();
	// 根据respcode进行合理的动作
	switch (respcode) {
			case 304:
			// 本地文件就是需要请求的文件
				break;
			case 200:
			// 对本地文件进行更新
			ContentUtils.outputFile(responseHtml, responseCheck.getEntity());
				break;
			case 404:
				// 发生错误
				responseHtml = new File(ServerConf.ERROR_HTML);
					break;
				default:
					break;
				}
//设置最终的返回状态码
				responsecode = respcode;
			} else {
				int code = getDirectly(client, uri, responseHtml);
				responsecode=code;	
			}
//getDirectly的定义如下:
private int getDirectly(CloseableHttpClient client2, URI uri,
			File responseHtml) {
		// TODO Auto-generated method stub
		HttpGet get = null;
		try {
			get = new HttpGet(uri.toURL().toString());
		} catch (MalformedURLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		int code = 200;
		try {
			CloseableHttpResponse response = client2.execute(get);
			code = response.getStatusLine().getStatusCode();
			// 进行分析返回的code
			switch (code) {
			case HttpStatus.SC_OK:
				// 将数据写出文件
				ContentUtils.outputFile(responseHtml, response.getEntity()
						 );
				break;
			case HttpStatus.SC_NOT_FOUND:
				// 没有找到文件
				System.err.println("没有找到相关的网页");
				break;
			default:
				break;
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return 0;
	}
}

4,向客户端返回请求的数据

// 对客户端做出响应
OutputStream outputStream = httpExchange.getResponseBody();
// 读取文件响应客户端的请求
byte[] buffer = ContentUtils.getContent(responseHtml);
httpExchange.sendResponseHeaders(responsecode, buffer.length);
//httpExchange.getResponseHeaders().add("ContentTyep","text/html");
		
System.out.println(httpExchange.getResponseHeaders().values());
 //System.out.println(new String(buffer));
outputStream.write(buffer);
outputStream.close();

四、结果展示

设置好firefox的代理后进行网络访问

在程序不运行的情况下,用firefox访问的结果如下:

图3 不运行代理服务器的时候火狐请求网页结果

接下来,运行代理服务器,进行在进行数据的访问:

图4 代理服务器运行时请求数据

接下来对交大bbs进行访问,得到如下的结果:

图4 访问交大BBS的结果

后台运行后缓存的数据截图如下:


图5 访问缓存数据的主机文件夹

主机下的具体的内容如下:


图6 进入百度主机文件夹下的文件(MD5处理后的文件名)

PS:大家在实现的时候注意:

读写图片文件的时候,reader和stream的结果是完全不同的,reader取出来 的图片一般式不能正确显示的,而stream才是正确的处理方式,是因为reader是按字符读取的,而stream是按照字节读取的。

整个工程的下载链接在百度网盘,有需要学习交流的可以下载:

链接: http://pan.baidu.com/s/1dDoc8QP 密码: rrpm



© 著作权归作者所有

zhangwenwen
粉丝 6
博文 31
码字总数 22973
作品 0
西安
其他
私信 提问
Spring Boot实践--RestTemplate使用

spring web 项目提供的RestTemplate,使java访问url更方便,更优雅。 它是spring提供的异步的客户端http访问的核心class,它提供非常简单的RESTful方式与http server端进行数据交互,根据所提...

spinachgit
2018/06/26
9
0
六大Web负载均衡原理与实现

开头先理解一下所谓的“均衡” 不能狭义地理解为分配给所有实际服务器一样多的工作量,因为多台服务器的承载能力各不相同,这可能体现在硬件配置、网络带宽的差异,也可能因为某台服务器身兼...

银杏果果
2016/12/26
2K
10
ksfzhaohui/shadowsocks-netty

基于netty4.0实现的shadowsocks客户端 使用 1.maven打包,生成shadowsocks-netty-0.1.0-alpha-bin.zip包 2.解压zip包,conf/config.xml中配置shadowsocks服务器地址 3.执行shell文件夹中的b...

ksfzhaohui
2016/11/27
0
0
六种常用的web负载均衡技术

不能狭义地理解为分配给所有实际服务器一样多的工作量,因为多台服务器的承载能力各不相同,这可能体现在硬件配置、网络带宽的差异,也可能因为某台服务器身兼多职,我们所说的“均衡”,也就...

stone_
2016/05/04
647
1
lnmp安装配置

一、总体简介 Lnmp架构(Linux+nginx+mysql+php)是目前网站的主流架构,这个架构包含了一个网站的最基本要求:运行环境+web容器+动态页面处理+存储。当然同样主流的架构还有lamp,但是个人认...

wx584a086dd4e8a
2017/06/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Java 文件类操作API与IO编程基础知识

阅读目录: https://www.w3cschool.cn/java/java-io-file.html Java 文件 Java 文件 Java 文件操作 Java 输入流 Java 输入流 Java 文件输入流 Java 缓冲输入流 Java 推回输入流 Java 数据输入...

boonya
35分钟前
5
0
SDKMAN推荐一个好

是在大多数基于Unix的系统上管理多个软件开发工具包的并行版本的工具。它提供了一个方便的命令行界面(CLI)和API来安装,切换,删除和列出sdk相关信息。以下是一些特性: By Developers, fo...

hotsmile
今天
9
0
什么是 HDFS

是什么? HDFS 是基于 Java 的分布式文件系统,允许您在 Hadoop 集群中的多个节点上存储大量数据。 起源: 单机容量往往无法存储大量数据,需要跨机器存储。统一管理分布在集群上的文件系统称...

Garphy
今天
5
0
一起来学Java8(四)——复合Lambda

在一起来学Java8(二)——Lambda表达式中我们学习了Lambda表达式的基本用法,现在来了解下复合Lambda。 Lambda表达式的的书写离不开函数式接口,复合Lambda的意思是在使用Lambda表达式实现函...

猿敲月下码
今天
11
0
debian10使用putty配置交换机console口

前言:Linux的推广普及,需要配合解决实际应用方能有成效! 最近强迫自己用linux进行实际工作,过程很痛苦,还好通过网络一一解决,感谢各位无私网友博客的帮助! 系统:debian10 桌面:xfc...

W_Lu
今天
12
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部