接下来都是我对HTTP代理服务器的理解。
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;
static public int BUFSIZ = 1024;
static public boolean logging = false;
static public OutputStream log = null;
protected 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;
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);
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];
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) {
}
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) {
}
}
} catch (Exception e0) {
}
}
static public void proxyStart(int port, Class<TextHttpProxy> clobj) {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(port);
while (true) {
Class[] objClass = new Class[1];
Object[] obj = new Object[1];
objClass[0] = Socket.class;
try {
Constructor cons = clobj.getDeclaredConstructor(objClass);
obj[0] = serverSocket.accept();
cons.newInstance(obj);
} 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是发动机,提供了网络通信的能力。
如果以后懂得更多,会及时补充~
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)