JSocketProxy:在线代理的实现
这次的目标是实现一个在线代理程序,类似www.proxyie.cn这样的就行啦
但是后台是要求两台服务器间通过socket交互,结构如下图所示:

作为一个简单模型,只要考虑能传输文字就好啦
首先是服务器B端
服务器B端的主要任务是:接受服务器A的socket连接,然后向目标网站发出请求,并将结果返回给服务器A。
1.接收Socket连接
public void do_server() throws IOException
{
server=new ServerSocket(8888);
while(true)
{
Socket client=server.accept();//若客户机提请求,socket连接
new Thread(new ServerThread(client)).start();
}
}
我采用的是多线程的方法,每次接收到客户端的请求,就新建一个ServerThread的线程用于交互
2.请求目标网站
而ServerThread每次检测到有服务器A发出的消息,就读取之,我设定的是三个参数:URL,COOKIE以及USER-AGENT
之后ServerThread调用getResponse发出请求并取得目标网站的响应,并返回相应内容;ServerThread于是可以将内容写回Socket供服务器A读取
public String getResponse(String url,String cookie,String UA) throws ClientProtocolException, IOException
{
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(url);//新建一个Http的Get请求
httpget.setHeader("Cookie", cookie);//定义Request头部中的Cookie
httpget.setHeader("User-Agent", UA);//定义Request头部中的User-Agent
System.out.println("executing request " + httpget.getURI());
HttpResponse responseBody = httpclient.execute(httpget);//执行请求 获取response
HttpEntity en = responseBody.getEntity();//取得response的body
String all = EntityUtils.toString(en);//将response内容转化为字符串
httpclient.getConnectionManager().shutdown();//关闭http请求
return all;
}
事实上由于返回的内容不一定是文本(有可能是图像什么的),可以选择在socket之间通过字节流交互
由于本项目的设定,在服务器B取得目标网站的内容并写入socket后,就可以直接关闭这个socket连接,至于原因,后面会谈到
其次是服务器A端
作为一个Web Project, 可以通过建立一个Servlet来处理用户的请求。即用户将请求的URL作为参数传给Servlet,而后Servlet建立到服务器B的Socket连接,将必要的参数发送过去(或者干脆以字节流的方式将Request发送过去),然后等待服务器B发送回来的数据,处理一下就可以输出,用户就能看到了。
注意关键词等待,因为数据通过socket往返必然不会是瞬间完成的,需要有一个While(True)的循环无限等待,直到确认读入完毕才能终止循环。于是我们之间在服务器端B设定的输出完立刻结束Socket连接就很方便了。服务器A的Servlet确认连接被服务器关闭后就可以停止输出并在用户的浏览器上显示了。
其实这种方式比较粗鲁,也有很多很好的解决方法,大家可以思考~
ServletA取得了数据后,需要做哪些处理呢?
首先是网页内的链接的URL替换,将相对地址替换成绝对地址
假定我们的Servlet地址是http://localhost/Servlet?url=http://url
当前请求的地址是http://baidu.com/index.html(即此时服务器A无法直接访问到baidu.com)
则<a href=”o.html”>此链接指向同名目录</a> 表示指向http://baidu.com/o.html
于是我们要利用正则表达式换成 <a href=”http://baidu.com/o.html”>此链接指向同名目录</a>
但是我们依旧无法直接访问baidu.com,那么我们应当修改成 <a href=”http://localhost/Servlet?url=http://baidu.com/o.html”>此链接指向同名目录</a>
这样就OK了。
这些操作都可以直接使用正则表达式来确定,例如上面的功能可以通过两次正则表达式替换来确定。
String res="<a href=\"o.html\" ></a><a href=\"http://baidu.com/o.html\" ></a>";
String host="http://baidu.com/";
res = res.replaceAll("(<a.*?href=\")([^http://][^\"]+\".*?>)", "$1"+host+"$2");
res = res.replaceAll("(<a.*?href=\")([http://][^\"]+\".*?>)", "$1http://localhost/Servlet?url=$2");
System.out.println(res);
//输出:<a href="http://localhost/Servlet?url=http://baidu.com/o.html" ></a><a href="http://localhost/Servlet?url=http://baidu.com/o.html" ></a>
那么还有哪些是需要考虑的呢?
CSS文件(link 标签)
形如:<link rel=”stylesheet” type=”text/css” href=”xxx.css“/>
JAVASCRIPT文件(script标签)
形如:<script src=”xxx.js” ></script>
图像(Img标签)
形如 <img src=”xxx.png” />
CSS文件中的background标签
background:url(xxx.jpg) ;
对应的正则表达式类似上面就行了
看到这里,就发现了我们的Servlet不仅要中转网页文本,还需要中转图像文件
于是在request头里的”Content-Type“需要修改为对应的参数
这样一个基本的在线代理就实现了,有啥问题欢迎指出!
然后,就没有然后了。





lz威武~~~~~~~~建冬哥很满意