??????? 1)我們要實(shí)現(xiàn)的就是把客戶的請(qǐng)求轉(zhuǎn)發(fā)到別一服務(wù)器.
要實(shí)現(xiàn)這個(gè)功能就是實(shí)現(xiàn)一個(gè)中間代理,中間代理,我們使用serverSocket 服務(wù)器實(shí)現(xiàn)端口幀聽(tīng),
ServerSocket serverSocket = new ServerSocket(listenPort);
也就是我說(shuō)我們請(qǐng)求的實(shí)際地址為A服務(wù)器,但是事實(shí)上這個(gè)請(qǐng)求是被轉(zhuǎn)發(fā)到B服務(wù)器去完成的,也就是A和B進(jìn)行了socket通信,然后把返回的數(shù)據(jù)再次傳回客戶端.
在這里我們使用了線程池來(lái)運(yùn)行客戶端請(qǐng)求,原因在于如果我們不使用線程的話.我們?cè)谑褂胹ocket = serverSocket.accept();
后會(huì)只有處理完一個(gè)連接后才能再次調(diào)用accept,因此在此使用線程池來(lái)運(yùn)行連接后的數(shù)據(jù)處理.
final ExecutorService tpe=Executors.newCachedThreadPool();
=====================================================
public static void main(String[] args) throws Exception {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
ServerSocket serverSocket = new ServerSocket(listenPort);
final ExecutorService tpe=Executors.newCachedThreadPool();
System.out.println("Proxy Server Start At "+sdf.format(new Date()));
System.out.println("listening port:"+listenPort+"……");
System.out.println();
System.out.println();
while (true) {
Socket socket = null;
try {
socket = serverSocket.accept();
socket.setKeepAlive(true);
tpe.execute(new ProxyTask(socket));
System.out.println("----1");
} catch (Exception e) {
e.printStackTrace();
}
}
}
運(yùn)行了代理服務(wù)器后,我們關(guān)鍵的在于轉(zhuǎn)發(fā),怎么把客戶的請(qǐng)求轉(zhuǎn)發(fā)出去,這里我們?nèi)サ魧?duì)頭部的處理,我們只是簡(jiǎn)單的轉(zhuǎn)發(fā),其實(shí)轉(zhuǎn)向很簡(jiǎn)單,只是在代理服務(wù)器開(kāi)啟一個(gè)新的連接服務(wù)器的socket
這里我們不去分析原請(qǐng)求的實(shí)現(xiàn)IP和端口,我們寫(xiě)成固定的地址,如:
socketOut = new Socket("172.29.1.99", Integer.parseInt("80"));
這樣我們只要把客戶端得到的inputStream然后寫(xiě)到指定服務(wù)器端的outputStream就行了.然后再把服務(wù)端返回的inputStream寫(xiě)到客戶端的outputStream中去就行了.
這里要注意的有兩點(diǎn).
1)我們?cè)诮邮詹⑥D(zhuǎn)發(fā)和服務(wù)器返回并轉(zhuǎn)發(fā)要用線程分開(kāi),不能在同一線程中,因?yàn)榭赡軆蓚€(gè)過(guò)程是同步進(jìn)行的.
2)我們要實(shí)現(xiàn)代理,就必須要取得原有請(qǐng)求的實(shí)際IP地址和PORT號(hào),因此我們分析請(qǐng)求的頭部,要分析頭部,我們必須要讀取頭部的字節(jié)來(lái)分析.要讀取inputstream后再把inputstream轉(zhuǎn)發(fā)時(shí)肯定會(huì)少了這部分?jǐn)?shù)據(jù),因?yàn)樵陬^部分析時(shí)已讀取出來(lái)了,因此為了正確的發(fā)送我們必須要把頭部的數(shù)據(jù)重新加入inputsteam中去,然頭在轉(zhuǎn)發(fā)前選把頭部數(shù)據(jù)寫(xiě)入到服務(wù)器的連接中去,這樣才正常,除非我們不去處理頭部.
=======================================================================================================================
public void run() {
System.out.println("----2");
StringBuilder builder=new StringBuilder();
try {
builder.append("\r\n").append("Request Time? :" + sdf.format(new Date()));
InputStream isIn = socketIn.getInputStream();
OutputStream osIn = socketIn.getOutputStream();
// 查找主機(jī)和端口
socketOut = new Socket("172.29.1.99", Integer.parseInt("80"));
socketOut.setKeepAlive(true);
InputStream isOut = socketOut.getInputStream();
OutputStream osOut = socketOut.getOutputStream();
//新開(kāi)一個(gè)線程將返回的數(shù)據(jù)轉(zhuǎn)發(fā)給客戶端,串行會(huì)出問(wèn)題慨亲,尚沒(méi)搞明白原因
Thread ot = new DataSendThread(isOut, osIn);
ot.start();
//讀取客戶端請(qǐng)求過(guò)來(lái)的數(shù)據(jù)轉(zhuǎn)發(fā)給服務(wù)器
readForwardDate(isIn, osOut);
//等待向客戶端轉(zhuǎn)發(fā)的線程結(jié)束
ot.join();
} catch (Exception e) {
e.printStackTrace();
if(!socketIn.isOutputShutdown()){
//如果還可以返回錯(cuò)誤狀態(tài)的話,返回內(nèi)部錯(cuò)誤
try {
socketIn.getOutputStream().write(SERVERERROR.getBytes());
} catch (IOException e1) {}
}
} finally {
try {
if (socketIn != null) {
socketIn.close();
}
} catch (IOException e) {}
if (socketOut != null) {
try {
socketOut.close();
} catch (IOException e) {}
}
//紀(jì)錄上下行數(shù)據(jù)量和最后結(jié)束時(shí)間并打印
builder.append("\r\n").append("Up? ? Bytes? :" + totalUpload);
builder.append("\r\n").append("Down? Bytes? :" + totalDownload);
builder.append("\r\n").append("Closed Time? :" + sdf.format(new Date()));
builder.append("\r\n");
logRequestMsg(builder.toString());
}
}