⾃⼰⽤java写⼀个http和https代理服务器
本⽂是基于socket实现的http,https代理服务器,资源利⽤率上肯定也是没有nio实现的效率要好。但是,秉持学习的态度,我还是来来实践⼀下。当然,如果这个实现的代理器只是你⾃⼰⽤的话或者少数⼏个⼈⽤的话,我觉得完全没问题,⾃⼰也试了,看视频啥的也没啥问题(如果你看的视频需要全部下载到本地后才能播放,那就只能把socket的过期时间设置长点了,不过现在⼀般都是可以缓冲⼀段就可以播放了,所以你⾃⼰斟酌吧——当别⼈还在⽤⽹上下的shadowsock来时,你⾃⼰⽤⾃⼰写的脚本来流畅的看视频的感觉肯定会不⼀样吧哈哈,后续如果有时间的话,我也会实现代理客户端来实现⾝份认证,别让坏蛋占⽤你的带宽。)
⾸先,我觉得你很有必要去看看http协议详解,⽹上有很多,⼤家⾃⾏查询,这⾥我简单说下主要使⽤了http的请求⾏(请求⽅法host 协议版本),请求头(host,keep-alive等等),⾄于https,其实就是http和ssl结合,这⾥的结合⽐较巧妙,因为考虑到安全和性能,https会事先请求建⽴socket链接,同时https利⽤http进⾏牵⼿双⽅交换了客户端传递信息加密的秘钥(就是这个过程使⽤了ssh),以后传递的消息都在客户端和服务器端建⽴的这个socket并且利⽤牵⼿约定好的秘钥进⾏加密消息传递信息,这时你要是解析这些消息会发现全是乱码(包括请求头等都经过加密)。我这⾥只是尽量简约但是⽐较形象的解释了https与http的关系,以便让读者可很⽅便的编程实现,详细的过程可以去⾃查http详解。
有了上⾯的知识储备,实现http代理服务器,其实就是代理服务器去解析http请求头消息到⽬标服务器,然后建⽴代理服务器到⽬标服务器的socket连接,将http的全部消息全部转发给⽬标服务器即可。⽽针对https的代理,主要是实现牵⼿过程即可,这个牵⼿过程主要是利⽤http请求获取⽬标服务器地址来建⽴socket连接,并在建⽴连接的同时告诉客户端连接已经建⽴好(返回HTTP/1.1 200 Connection Established\r\n\r\n),接下来客户端就会⾃动通过代理服务器建⽴的与⽬标服务器的连接发消息给⽬标服务器进⾏交换秘钥,然后,也会利⽤此连接传递消息,当然,传递消息时代理服务器只需要转发即可(你也看不懂加密后传递的消息)。
下⾯给出我的代码,当然欢迎⼤家给出意见或建议,在此先谢谢⼤家:
public class test {
@SuppressWarnings("resource")
public static void main(String[] s) {
ServerSocket ss = null;
try {
ss = new ServerSocket(11111);
} catch (IOException e1) {
e1.printStackTrace();
}
while(true) {
try {
Socket socket = ss.accept();
socket.setSoTimeout(1000*60);//设置代理服务器与客户端的连接未活动超时时间
String line = "";
InputStream is = InputStream();
String tempHost="",host;
int port =80;
String type=null;
OutputStream os = OutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// System.out.println("========+++++++++++++=======");
int temp=1;
StringBuilder sb =new StringBuilder();
while((line = br.readLine())!=null) {
System.out.println(line+"-----------------");
怎样设置代理服务器if(temp==1) { //获取请求⾏中请求⽅法,下⾯会需要这个来判断是http还是https
// System.out.println("+++++++++"+line);
type = line.split(" ")[0];
if(type==null)continue;
}
temp++;
String[] s1 = line.split(": ");
if(line.isEmpty()) {
break;
}
for(int i=0;i<s1.length;i++) {
if(s1[i].equalsIgnoreCase("host")) {
if(s1[i].equalsIgnoreCase("host")) {
tempHost=s1[i+1];
}
}
sb.append(line + "\r\n");
line=null;
}
sb.append("\r\n"); //不加上这⾏http请求则⽆法进⾏。这其实就是告诉服务端⼀个请求结束了
if(tempHost.split(":").length>1) {
port = Integer.valueOf(tempHost.split(":")[1]);
}
host = tempHost.split(":")[0];
Socket proxySocket = null;
if(host!=null&&!host.equals("")) {
proxySocket = new Socket(host,port);
proxySocket.setSoTimeout(1000*60);//设置代理服务器与服务器端的连接未活动超时时间
OutputStream proxyOs = OutputStream();
InputStream proxyIs = InputStream();
if(type.equalsIgnoreCase("connect")) { //https请求的话,告诉客户端连接已经建⽴(下⾯代码建⽴)
os.write("HTTP/1.1 200 Connection Established\r\n\r\n".getBytes());
os.flush();
}else {//http请求则直接转发
proxyOs.String().getBytes("utf-8"));
proxyOs.flush();
}
new ProxyHandleThread(is, proxyOs,host).start();//监听客户端传来消息并转发给服务器
new ProxyHandleThread(proxyIs, os,host).start(); //监听服务器传来消息并转发给客户端
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class ProxyHandleThread extends Thread {
private InputStream input;
private OutputStream output;
private String host;//debug时需要的东西,实际不需要
public ProxyHandleThread(InputStream input, OutputStream output,String host) {
this.input = input;
this.output = output;
this.host = host;
}
@Override
public void run() {
try {
while (true) {
BufferedInputStream bis = new BufferedInputStream(input);
byte[] buffer =new byte[1024];
int length=-1;
while((ad(buffer))!=-1) {//这⾥最好是字节转发,不要⽤上⾯的InputStreamReader,因为https传递的都是密⽂,那样会乱码,消息传到服务器端也会出 output.write(buffer, 0, length);
length =-1;
// System.out.println("客户端通过代理服务器给服务器发送消息"+input+host);
}
output.flush();
try {
Thread.sleep(10); //避免此线程独占cpu
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (SocketTimeoutException e) {
try {
input.close();
output.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}catch (IOException e) {
System.out.println(e);
}finally {
try {
input.close();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
写代码的过程中有些地⽅可能会有疑问,不要略过它们,因为它们能让你对问题认识更加深刻。我也在想把http和https分开,因为好多http的Connection不是keep-alive,那么就可以在进⾏⼀次请求响应后就可以断开连接,这样就可以提前断开socket连接结束线程,更早的释放资源了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论