多執行緒Http代理伺服器 Java實現
最近心血來潮,想熟悉一下Socket程式設計,就在網上看了一些資料,對Socket有了一個比較基本的瞭解,無意間竟發現用Java開發的簡易HttpProxy的Demo,現對源程式進行了一些小的修改,使可讀性變強,以供大家參考。
import java.io.*;
import java.net.*;
public class MyHttpProxy extends Thread {
static public int CONNECT_RETRIES=5; //嘗試與目標主機連線次數
static public int CONNECT_PAUSE=5; //每次建立連線的間隔時間
static public int TIMEOUT=50; //每次嘗試連線的最大時間
static public int BUFSIZ=1024; //緩衝區最大位元組數
static public boolean logging = false; //是否記錄日誌
static public OutputStream log_S=null; //日誌輸出流
static public OutputStream log_C=null; //日誌輸出流
// static public String LOGFILENAME_S="log_S.txt";
// static public String LOGFILENAME_C="log_C.txt";
// 與客戶端相連的Socket
protected Socket csocket;
public MyHttpProxy(Socket cs) {
csocket=cs;
start();
}
public void writeLog(int c, boolean browser) throws IOException {
if(browser) log_C.write((char)c);
else log_S.write((char)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 void run(){
String buffer = ""; //讀取請求頭
String URL=""; //讀取請求URL
String host=""; //讀取目標主機host
int port=80; //預設埠80
Socket ssocket = null;
//cis為客戶端輸入流,sis為目標主機輸入流
InputStream cis = null,sis=null;
//cos為客戶端輸出流,sos為目標主機輸出流
OutputStream cos = null,sos=null;
try{
csocket.setSoTimeout(TIMEOUT);
cis=csocket.getInputStream();
cos=csocket.getOutputStream();
while(true){
int c=cis.read();
if(c==-1) break; //-1為結尾標誌
if(c=='\r'||c=='\n') break;//讀入第一行資料
buffer=buffer+(char)c;
if (logging) writeLog(c,true);
}
//抽取URL(http://www.baidu.com/)
URL=getRequestURL(buffer);
int n;
//抽取host
n=URL.indexOf("//");
if (n!=-1)
host=URL.substring(n+2); // www.baidu.com/
n=host.indexOf('/');
if (n!=-1)
host=host.substring(0,n);// www.baidu.com
// 分析可能存在的埠號
n=host.indexOf(':');
if (n!=-1) {
port=Integer.parseInt(host.substring(n+1));
host=host.substring(0,n);
}
int retry=CONNECT_RETRIES;
while (retry--!=0) {
try {
ssocket=new Socket(host,port); //嘗試建立與目標主機的連線
break;
} catch (Exception e) { }
// 等待
Thread.sleep(CONNECT_PAUSE);
}
if(ssocket!=null){
ssocket.setSoTimeout(TIMEOUT);
sis=ssocket.getInputStream();
sos=ssocket.getOutputStream();
sos.write(buffer.getBytes()); //將請求頭寫入
pipe(cis,sis,sos,cos); //建立通訊管道
}
}catch(Exception e){
e.printStackTrace();
}
finally {
try {
csocket.close();
cis.close();
cos.close();
}
catch (Exception e1) {
System.out.println("\nClient Socket Closed Exception:");
e1.printStackTrace();
}
try {
ssocket.close();
sis.close();
sos.close();
}
catch (Exception e2) {
System.out.println("\nServer Socket Closed Exception:");
e2.printStackTrace();
}
}
}
public String getRequestURL(String buffer){
String[] tokens=buffer.split(" ");
String URL="";
for(int index=0;index<tokens.length;index++){
if(tokens[index].startsWith("http://")){
URL=tokens[index];
break;
}
}
return URL;
}
public void pipe(InputStream cis,InputStream sis,OutputStream sos,OutputStream cos){
try {
int length;
byte bytes[]=new byte[BUFSIZ];
while (true) {
try {
if ((length=cis.read(bytes))>0) {
sos.write(bytes,0,length);
if (logging) writeLog(bytes,0,length,true);
}
else if (length<0)
break;
}
catch(SocketTimeoutException e){}
catch (InterruptedIOException e) {
System.out.println("\nRequest Exception:");
e.printStackTrace();
}
try {
if ((length=sis.read(bytes))>0) {
cos.write(bytes,0,length);
if (logging) writeLog(bytes,0,length,false);
}
else i[align=left][/align]f (length<0)
break;
}
catch(SocketTimeoutException e){}
catch (InterruptedIOException e) {
System.out.println("\nResponse Exception:");
e.printStackTrace();
}
}
} catch (Exception e0) {
System.out.println("Pipe異常: " + e0);
}
}
}
下面這張圖可以清晰地闡明HttpProxy的實現原理:
其中流程具體如下:
1、客戶端通過瀏覽器向代理伺服器傳送HttpRequest(GET/POST);
2、代理伺服器讀取請求頭,抽取出請求的具體目標伺服器HOST和PORT;
3、代理伺服器把請求頭髮送給目標伺服器;
4、代理伺服器建立管道,供客戶端和目標伺服器通過兩個Socket通訊。
遺留問題:
最終經過測試,發現在訪問百度時會出現非常詭異的錯誤,不知道為何?
問題如下:
1、訪問百度主頁,正常顯示。
2、在百度主頁點選貼吧或者其他連結時會發現連結到一些非常詭異的頁面,不知道為何。還待以後研究。
簡單的測試
public static void startProxy(int port,Class clobj) {
try {
ServerSocket ssock=new ServerSocket(port);
while (true) {
Class [] sarg = new Class[1];
Object [] arg= new Object[1];
sarg[0]=Socket.class;
try {
java.lang.reflect.Constructor cons = clobj.getDeclaredConstructor(sarg);
arg[0]=ssock.accept();
cons.newInstance(arg); // 建立HttpProxy或其派生類的例項
} catch (Exception e) {
Socket esock = (Socket)arg[0];
try { esock.close(); } catch (Exception ec) {}
}
}
} catch (IOException e) {
System.out.println("\nStartProxy Exception:");
e.printStackTrace();
}
}
// 測試用的簡單main方法
static public void main(String args[]) throws FileNotFoundException {
System.out.println("在埠808啟動代理伺服器\n");
OutputStream file_S=new FileOutputStream(new File(LOGFILENAME_S));
OutputStream file_C=new FileOutputStream(new File(LOGFILENAME_C));
MyHttpProxy.log_S=file_S;
MyHttpProxy.log_C=file_C;
MyHttpProxy.logging=true;
MyHttpProxy.startProxy(808,MyHttpProxy.class);
}
程式碼如下:
相關文章
- Java多執行緒實現方式Java執行緒
- Java多執行緒的實現Java執行緒
- 多執行緒伺服器的實現執行緒伺服器
- java實現多執行緒的方法Java執行緒
- Java多執行緒的實現方法Java執行緒
- Java多執行緒【三種實現方法】Java執行緒
- Java多執行緒-基礎及實現Java執行緒
- JAVA多執行緒下載的實現Java執行緒
- 【Java】Java多執行緒實現的聊天客戶端和伺服器Java執行緒客戶端伺服器
- Java多執行緒——執行緒Java執行緒
- Java高併發與多執行緒(二)-----執行緒的實現方式Java執行緒
- Java實現多執行緒的三種方式Java執行緒
- 使用Java實現多執行緒程式設計Java執行緒程式設計
- 如何實現多執行緒執行緒
- 多執行緒原理實現執行緒
- Java多執行緒之守護執行緒實戰Java執行緒
- Java多執行緒-執行緒中止Java執行緒
- Java多執行緒——執行緒池Java執行緒
- 多執行緒實現多工二執行緒
- 多執行緒實現多工一執行緒
- 【Java多執行緒】輕鬆搞定Java多執行緒(二)Java執行緒
- Java多執行緒檔案分片下載實現Java執行緒
- Java建立多執行緒的幾種方式實現Java執行緒
- Java 多執行緒異常捕獲Runnable實現Java執行緒
- java——多執行緒Java執行緒
- java 多執行緒Java執行緒
- 【Java】多執行緒Java執行緒
- JAVA 多執行緒 ??Java執行緒
- java多執行緒Java執行緒
- Java - 多執行緒Java執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒-執行緒通訊Java執行緒
- Java多執行緒-執行緒狀態Java執行緒
- Java多執行緒(2)執行緒鎖Java執行緒
- java多執行緒9:執行緒池Java執行緒
- Java多執行緒之執行緒中止Java執行緒
- 【java多執行緒】(二)執行緒停止Java執行緒
- Java多執行緒——守護執行緒Java執行緒