HTTP代理服务(proxy server) 器就是客户端也是服务端,是一个事务处理的中间人,就像下图所展示的一样。
图片来源于《HTTP权威指南》
代理服务器的作用有很多,例如:儿童过滤器、文档访问控制、安全防火墙、web缓存等等。
以下,我用最基础的代码写一个简易的HTTP代理服务器,最后还有相关的解说,有助于更容易理解HTTP代理服务器的机制。
package testproxy;
import java.lang.reflect.Constructor;
import java.net.*;
import java.io.*;
public class TextHttpProxy extends Thread {
//操作实现代理服务器的类
static public int RETRIES = 5;
//在放弃之前尝试连接远程主机的次数
static public int PAUSE = 5;
//在两次连接尝试之间的暂停时间
static public int TIMEOUT = 50;
//等待Socket输入的等待时间
static public int BUFSIZ = 1024;
//输入的缓冲大小
static public boolean logging = false;
//是否要求代理服务器在日志中记录所有已传输的数据
static public OutputStream log = null;
//默认日志例程将向该OutputStream对象输出日志信息
protected Socket socket;
// 传入数据用的Socket
static private String parent = null;
//上级代理服务器
static private int parentPort = -1;
// 用来把一个代理服务器链接到另一个代理服务器(需要指定另一个服务器的名称和端口)。
static public void setParentProxy(String name, int port) {
parent = name;
parentPort = port;
}
public TextHttpProxy(Socket s) {
//创建一个代理线程
socket = s;
start();
//启动线程
}
public void writeLog(int c, boolean browser) throws IOException {
//写日志
log.write(c);
}
public void writeLog(byte[] bytes, int offset, int len, boolean browser)
throws IOException {
//循环写日志
for (int i = 0; i < len; i++)
writeLog((int) bytes[offset + i], browser);
}
// 默认情况下,日志信息输出到控制台或文件
public String printLog(String url, String host, int port, Socket sock) {
java.text.DateFormat cal = java.text.DateFormat.getDateTimeInstance();
System.out.println(cal.format(new java.util.Date()) + " - " + url + " "
+ sock.getInetAddress() + "\n");
return host;
}
public void run() {
// 执行操作的线程
String line;
String host;
int port = 80;
//默认端口为80
Socket outbound = null;
//每次请求都会创建一个新的线程
try {
socket.setSoTimeout(TIMEOUT);
//设置超时时间
InputStream is = socket.getInputStream();
//创建输入流
OutputStream os = null;
try {
line = "";
// 获取请求行的内容
host = "";
int state = 0;
boolean space;
while (true) {
//无限循环
int c = is.read();
//读取输入流的信息
if (c == -1)//没有读取信息
break;
if (logging)
writeLog(c, true);
//将信息写入日志
space = Character.isWhitespace((char) c);
//判断是否为空白字符
switch (state) {
//判断状态
case 0:
if (space)
continue;
//跳过本次循环
state = 1;
//更改状态
case 1:
if (space) {
state = 2;
continue;
//跳过本次循环
}
line = line + (char) c;
//添加读取的信息
break;
case 2:
if (space)
continue;
// 跳过空白字符
state = 3;
//更改状态
case 3:
if (space) {
//如果是空白字符
state = 4;
//更改状态
String host0 = host;
//取出网址
int n;
n = host.indexOf("//");
//获取网址(不包括协议)
if (n != -1)
//没有找到
host = host.substring(n + 2);
n = host.indexOf('/');
if (n != -1)
//没有找到/
host = host.substring(0, n);
n = host.indexOf(":");
// 分析可能存在的端口号
if (n != -1) {
//没有找到:
port = Integer.parseInt(host.substring(n + 1));
host = host.substring(0, n);
}
host = printLog(host0, host, port, socket);
//获得网站域名
if (parent != null) {
host = parent;
port = parentPort;
}
int retry = RETRIES;
while (retry-- != 0) {
try {
outbound = new Socket(host, port);
//创建连接对象,通向目标服务器
break;
} catch (Exception e) {
System.out.println("无法创建连接:"+e.getMessage());
}
Thread.sleep(PAUSE);
//设置线程等待
}
if (outbound == null)
break;
outbound.setSoTimeout(TIMEOUT);
//设置超时时间,防止read方法导致的组赛
os = outbound.getOutputStream();
//获得输出流对象
os.write(line.getBytes());
//将信息写入流
os.write(' ');
os.write(host0.getBytes());
//将信息写入流
os.write(' ');
writeInfo(is, outbound.getInputStream(), os, socket
.getOutputStream());
//调用方法将信息写入日志,套接字数据的交换
break;
}
host = host + (char) c;
break;
}
}
} catch (IOException e) {
}
} catch (Exception e) {
} finally {
try {
socket.close();
//释放资源
} catch (Exception e1) {
}
try {
outbound.close();
} catch (Exception e2) {
}
}
}
void writeInfo(InputStream is0, InputStream is1, OutputStream os0,
OutputStream os1) throws IOException {
//读取流中信息写入日志
try {
int ir;
byte bytes[] = new byte[BUFSIZ];
//创建字节数组,大小:1024
//也是定影socket缓冲区的大小
while (true) {
try {
if ((ir = is0.read(bytes)) > 0) {
//判断读取输入流的信息
os0.write(bytes, 0, ir);
//将读取的数据写入输出流对象中
if (logging)
writeLog(bytes, 0, ir, true);
//写入日志
} else if (ir < 0)
//读取完毕
break;
} catch (InterruptedIOException e) {
//捕获中断IO流异常
}
try {
if ((ir = is1.read(bytes)) > 0) {
//判断读取输入流的信息
os1.write(bytes, 0, ir);
//将读取的数据写入输出流对象中
if (logging)
writeLog(bytes, 0, ir, false);
//写入日志
} else if (ir < 0)
//读取完毕
break;
} catch (InterruptedIOException e) {
//捕获中断IO流异常
}
}
} catch (Exception e0) {
//捕获异常
}
}
static public void proxyStart(int port, Class
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(port);
//根据端口创建服务器端Socket对象
while (true) {
Class[] objClass = new Class[1];
//创建类数组,大小为1
Object[] obj = new Object[1];
//创建对象数组,大小为1
objClass[0] = Socket.class;
//添加Socket类
try {
Constructor cons = clobj.getDeclaredConstructor(objClass);
//创建代理服务器实例
obj[0] = serverSocket.accept();
//挂起等待客户的请求
cons.newInstance(obj);
// 创建TextHttpProxy或其派生类的实例 创建传入类
} catch (Exception e) {
Socket socket = (Socket) obj[0];
//对象强制转换
try {
socket.close();
//释放资源
} catch (Exception ec) {
}
}
}
} catch (IOException e) {
}
}
static public void main(String args[]) {
System.out.println("HTTP代理服务器已经成功启动!");
TextHttpProxy.log = System.out;
//日志信息输出到控制台
TextHttpProxy.logging = false;
TextHttpProxy.proxyStart(9080, TextHttpProxy.class);
//调用方法
}
}
代码解说:
开启一个服务器Socket 监听指定端口的请求,同时,代理服务器挂起等待客户端的请求。服务器的Socket监听到连接请求时,则开启一个新的线程处理这个连接请求,服务器的Socket 再次进入监听状态。
在连接线程时,代理服务器接受来自客户端的请求,并执行操作的线程,设置超时时间,解析客户端Host头域里面的值,获取目标web服务器地址,分析可能存在的端口号,建立socket将请求发送到远程服务器,然后将响应报文转发回原socket。最后把 socket 关闭,线程销毁。
写一个简单的main方法来进行代理服务器测试,并将日志信息显示在控制台。
代码解说图:
以下图片是测试效果:
使用firefox浏览器,手动设置代理服务器
运行代码,开启代理服务器,输入HTTP协议的URI,如图
能进入这个网站,说明你的代理成功了!
在控制台会输出以下信息
在这个实现HTTP代理服务器的过程中,socket 是非常重要的,它也叫做套接字。如下是它的作用:
socket (套接字)之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
如果不太懂HTTP和socket之间的联系,那就用传说中的比喻来说说:HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
相关资讯
什么是ip代理软件?可以修改ip地址吗?
经常碰到朋友问,代理IP是做啥的,有什么用。我对他一顿解释,然后他还是云里雾里,好吧,可能是我口才不好,这里写文再解释下。代理IP对于很多人来说,用处很大,他们
来源:黑核混拨ip加速器网
2018-12-27 17:27:18
现在玩自媒体需要用到ip代理吗?
如今越来越多的人开始使用微博、今日头条等等各种自媒体平台,因此很多公司很多人都看到了这之中的商机和机会,并且借助自媒体平台来发展自己的生意,也因为自媒体的蓬勃发
来源:黑核混拨ip加速器网
2018-12-27 17:37:53
互联网工作中ip代理是不可缺少的工具
目前,中国的互联网大军正在不断壮大,各种各样依托互联网的新兴行业正在兴起,哪怕是很多传统行业,为了抢占竞争的制高点,也将跟友商之间的竞争搬到了互联网平台之上。对
来源:黑核混拨ip加速器网
2018-12-27 17:45:23
使用代理ip有什么好处?
现在,高匿代理ip时代已经到来,但是,还是有很多人不了解高匿代理ip到底有什么用,虽然随着网络科技的发展,网络对我们的生活带来了很多方便,也带来了更多的选择,网
来源:黑核混拨ip加速器网
2018-12-28 15:00:45
爬虫技术工程师为什么离不开ip代理
说到爬虫,自然离不开爬虫代理地址。爬虫是一门随着互联网大数据而应运而生的产物,它主要是为了在海量的网络数据中采集分析有效的数据而诞生的一门技术。无需多言,爬虫技
来源:黑核混拨ip加速器网
2018-12-28 15:24:23
现在采集越来越难如何找到实用的HTTP代理IP
目前,中国的互联网大军正在不断壮大,各种各样依托互联网的新兴行业正在兴起,哪怕是很多传统行业,为了抢占竞争的制高点,也将跟友商之间的竞争搬到了互联网平台之上。对
来源:黑核混拨ip加速器网
2018-12-28 15:56:10
什么情况下需要ip代理软件 ip代理软件用途
对于网络营销推广和SEO的朋友来说,数据抓取和模拟访问都是必备的工作,这两种方式都需要有大量的IP资源支持,很多用户在一些网站上面批量注册帐号、发布留言评论都会
来源:黑核混拨ip加速器网
2018-12-28 16:28:33
国内好用又实惠的https代理ip服务器不要错过了
随着大数据时代的到来,爬虫已经成了获取数据的必不可少的方式,在使用爬虫多次爬取同一网站时,经常会被网站的IP反爬虫机制给禁掉,为了解决封禁IP的问题,通常会使用
来源:黑核混拨ip加速器网
2018-12-28 16:55:02
代理ip软件能帮助你实现优化网站
如今代理IP工具在营销领域,无论是主动式的发布推广信息,还是被动性反制竞争对手的恶意点击来说,都具有不可替代的作用。可细心的人们发现市面上代理IP的软件太多,如
来源:黑核混拨ip加速器网
2019-01-08 17:54:51
实用https代理服务器别人能查到真实地址吗
现在,高匿代理ip时代已经到来,但是,还是有很多人不了解高匿代理ip到底有什么用,虽然随着网络科技的发展,网络对我们的生活带来了很多方便,也带来了更多的选择,网
来源:黑核混拨ip加速器网
2018-12-28 17:02:47
推荐阅读
Copyright © | 广州网连信息技术有限公司动态ip代理版权所有 备案号:粤ICP备18088888号-15 网站地图