Freedom_Server
简介:一个代理服务器的简单实现
版本:V0.1
由于某些不可抗力,我们不可以直接访问美帝的google,但我们可以"曲线救国"-----通过代理服务器
项目原理:
- 虽然不能直接访问美国的服务器,但我们可以访问我国香港的服务器呀,香港的服务器可以访问美国的服务器,所以我们就可以在香港放置一个代理服务器。Ps:在这里强烈抗议某些分裂主义的人,我作为新时代的中国青年在这里十分坚定、十分认真地坚持一个中国,反对一切分裂主义??中国加油
- 我们通过访问代理服务器访问美帝的google,然后由代理服务器将数据返回给我们的浏览器
基础知识:
首先,我们要明白什么是代理服务器(Proxy)?
总的来说,代理服务器是一种重要的服务器安全功能
工作主要在OSI模型的会话层
下面我来讲一讲代理服务器的主要功能:
- 共享网络:使用代理服务器共享上网,可以提供企业级的文件缓存、复制、地址过滤等服务、充分利用局域网出口的有带宽,加快内网用户的访问速度,可以解决只有一条线路一个IP,IP资源不足,局域网多用户上网的功能。同时可以作为一个防火墙,隔离内网与外网,还可提供监控和记录信息传输的功能,加强了局域网的安全性,又便于对上网用户进行管理。
- 访问代理:加快访问房展的速度,网络出现拥挤或故障时,通过代理服务器访问目的网站。比如A访问C,但A、C之间网络出现问题,可以绕道到代理服务器B,由B访问C,再将结果转发给A。还有部分代理服务器并非本有大量的缓存文件,如果访问的数据在代理服务器的缓存中,可以直接读取,无需连接到远端Web服务器。这样就加快了访问速度。
- 防止攻击:刻意隐藏自己的真实地址,隐藏IP,防止被黑客攻击。举个最简单的例子,我们可以使用相应协议的代理服务器登录QQ,这样QQ客户端就不会通过IP解析来读取到我们的位置信息了
- 突破限制:科学上网你懂吧嘻嘻嘻
- 提高速度:提高下载速度、突破下载限制。比如有的网站提供下载资源,做了一IP一线程的限制。我们可以设置多线程,为每一个线程设置一个代理。对于限制一IP很好解决,只要使用不同的代理服务器就可以同时下载多个资源。又比如电信用户向上联通的电影网站,可以使用一个IP地址属于联通的代理服务器就可以成功使用了
所以代理服务器还是一个挺强大的东西,对吧?
之后,我将简单地实现一个代理服务器的功能
敬请期待~
原理图:
图画的有点丑哈哈哈,见谅,重点是理解核心思想好不啦
本地主机需要目标主机的数据,与代理服务器建立连接,然后代理服务器接收到请求之后与目标主机建立连接,然后将请求的数据存到本地没然后转发给本地主机,然后用户下次请求时,可以更快速的从缓存中返回数据
看看,这里的Cache就可以假想到Web的网络加速
当代理服务器拥有用户想要的数据时:
1、Client端向Server端发送一个数据需求包
2、Server端接收之后,对比这个包的来源于预计要前往的目标是否可接受。如果来源和目标都是合法的,Server端会替Client端获取资料,其中有一项政策(对比政策)
3、对比政策,Server对先检查自己的Cache(新的数据可能在内存中,比较旧的数据放在硬盘中),如果有Client所需要的数据,那么将数据准备取出,而不经过向Internet要求数据的程序。
4、将数据传回给Client端
其中,代理服务器对Cache的速度是有要求的,而这个Cache就是硬盘,硬盘不仅要大,还要足够快。
接下来,我们要明确不管使用哪种方式应用代理服务器,监控HTTP传输的过程总是如下
核心三步走
第一步:浏览器发送请求给代理服务器。(请求中包含目标的URL)
第二步:代理服务器读取该URL,并把请求转发给合适的目标服务器
第三步:代理服务器接收来自Internet目标机器的应答,把应答转发给合适的内部浏览器
设计规划:
代理服务器也不过是一种特殊的服务器,所以如果要处理多个请求,代理服务器就应该使用多线程
1、等待来自Client(浏览器)的请求
2、启动一个新的线程,处理Client的链接请求
3、读取浏览器请求的第一行(读取请求目标的URL)
4、分析请求第一行的内容,找到目标服务器的IP和端口号
5、打开一个通向目标服务器的Socket
6、把请求的第一行发送到输出Socket
7、把请求的剩余部分发送到输出Socket
8、把目标Web服务器返回的数据发送给Client浏览器
细节问题:
1、从Socket按行读取数据最适合进一步处理,但会产生性能瓶颈
2、两个Socket之间连接需要高效。有什么方法实现?
比如在数据进入的时候进行过滤,按行读取数据,但是如果数据一到达代理服务器就立即把它转发出去会不会效率更高一些?
比如传送和接收使用多个独立的线程,可以提高效率,但是线程大量的创建和拆除会不会带来性能问题?
所以,对每一个请求,用一个线程处理数据的接收和发送,同时,在数据到代理服务器的时候,尽快转发出去
项目结构:
main函数
import java.io.IOException;
import java.net.ServerSocket;
public class HttpProxy {
private ServerSocket myServerSocket; // 服务器Socket
private Thread myThread; // 链接守护线程
public HttpProxy(int port) throws IOException {
myServerSocket = new ServerSocket(port);
myThread = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
try {
// 等待HTTP会话链接,建立客户端Socket连接
new HTTPSession(myServerSocket.accept());
} catch (IOException e) { }
}
}
});
// 设置链接线程为Daemon线程,并启动。
myThread.setDaemon(true);
myThread.start();
}
/**
* 代理服务器主函数
*
* @param args
*/
public static void main(String[] args) {
try {
new HttpProxy(4444); // 启动代理服务器的端口
} catch (IOException e1) {
System.err.println("Couldn't start server:\n" + e1);
System.exit(-1);
}
System.out.println("Start proxy...(Get return to Stop proxy!)");
// 输入回车停止代理服务
try {
System.in.read();
} catch (IOException e) {
System.out.println("Stop proxy...");
}
}
}
部分代理实现过程
public class HTTPSession implements Runnable {
static long threadCount = 0;
private Socket clientSocket = null;
Thread t = null;
// HTTP 请求的最大字节数 1024*8=2^13
final static int bufsize = 8192;
byte[] buf = new byte[bufsize];
// Host类实例化对象
Host targethost = null;
// 接受到客户端Socket s连接
public HTTPSession(Socket s) {
// TODO Auto-generated constructor stub
clientSocket = s;
// 为该次会话建立一个Daemon线程
t = new Thread(this);
t.setDaemon(true);
t.start();
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("\t[+] HTTPSession.run():StartID : " + t.getId());
threadCount++;
try {
InputStream isInputStream = clientSocket.getInputStream();
if (isInputStream == null)
return;
// 首次读bufsize大小的isInputStream写到buf
int readll = GetHeaderToBuf(isInputStream, bufsize, buf, 0);
// 构建读取头输入流
ByteArrayInputStream bais = new ByteArrayInputStream(buf, 0, readll);
InputStreamReader isr = new InputStreamReader(bais);
BufferedReader br = new BufferedReader(isr);
// 从请求头流读取数据
targethost = new Host();
ReadHeaderData(br, targethost);
// 根据主机信息,计算出IP地址和端口号
targethost.cal();
System.out.println("\t\t[+] Address:[" + targethost.address
+ "]Port:" + targethost.port);
// 客户端请求链接中转管道
System.out.println("\t\t[+] Pipe Start: -----------------");
try {
Pipe(buf, readll, clientSocket.getInputStream(), clientSocket
.getOutputStream(), targethost);
} catch (Exception e) {
System.out.println("\t\t[#] Pipe Exception!" + e.toString());
// e.printStackTrace();// print red color
}
System.out.println("\t\t[-] Address:[" + targethost.address
+ "]Port:" + targethost.port);
System.out.println("\t\t[-] Pipe End : -----------------");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("\t[-] HTTPSession.run():End ID : " + t.getId());
System.out.println("\t[*] ThreadCount:" + --threadCount);
}// run()
/**
* // 从请求头流读取数据
*
* @param br
* @param targethost
* @throws IOException
*/
private void ReadHeaderData(BufferedReader br, Host targethost)
throws IOException {
...
}
/**
* // 首次读bufsize大小的isInputStream写到buf
*
* @param isInputStream
* @param bufsize
* @param buf
* @param readll
* @return
* @throws IOException
*/
private int GetHeaderToBuf(InputStream isInputStream, final int bufsize,
byte[] buf, int readll) throws IOException {
...
}
/**
* 找Http请求头的结束位置
*
* @param buf
* @param readll
* @return
*/
private int FindHeaderEnd(final byte[] buf, int readll) {
...
}
/**
* // 客户端请求链接中转管道
*
* @param requesthead
* @param requestLen
* @param clientIS
* @param clientOS
* @param targethost
* @throws IOException
*/
void Pipe(byte[] requesthead, int requestLen, InputStream clientIS,
OutputStream clientOS, Host targethost) throws IOException {
...
}
}
如何设置浏览器的代理信息呢?
首先,进入浏览器,以Chrome为例
点击进入设置,进入高级,系统
打开代理设置,在地址栏输入代理服务器的IP和端口号
这里使用的本地IP和端口4444来进行测试
设置完之后,在浏览器输入的网址访问信息都通过代理服务,代理服务器去访问目标网站啦
效果展示:
自己搭建一个代理服务器,岂不美哉,虽然我没有在香港的服务器,但是我尝试点一下google还是勇气可嘉的哈哈哈哈
说在最后
其实我觉得技术是无辜的,不要说这个东西好还是坏,关键使用他们的人是怎样的。你可以用它去造福全民,也可以用它搅得一片混沌。这是一把双刃剑,既可以用来保护隐私,也可以用来窥探隐私;我觉得代理服务器还是有前景的,毕竟有这么多的优点,合理的利用一定能造福人间哈哈哈。我也相信随着全球一体化的进程的逐步推进,总会有拨开云雾见青天的一天哈哈哈。