From Gossip@Openhome

Java Gossip: 代理伺服器

代理伺服器的作用,就是作為連線來源端與連線目的端之間的橋樑,代理伺服器的功能有很多種,有作為網頁快取的代理伺服器,有作為防火牆功能的代理伺服器,有作為訊息過濾的代理伺服器等等。

客戶端 <---->  代理伺服器  <---->  目的伺服器

其實將代理伺服器的功能簡化至最基本時,其功能就是將連線來源端的訊息轉接至連線目的端,而連線目的端的訊息轉接至連線來源端,對連線來源端而言,代理伺服器像是伺服端,對連線目的端而言,代理伺服器像是客戶端。

訊息在代理伺服器時所作的處理,決定了代理伺服器的種類,如果它將網頁暫存在伺服器本身的儲存裝置,並供客戶端直接比對下載,它的作用就是網頁代理伺服 器,如果它的作用在過濾進出代理伺服器的訊息,它的作用就有些像是防火牆的功能(當然必須實作低階的封包過濾才算真正是),您也可以設計一個代理伺服器, 專門過濾掉網頁上的廣告部份。

下面這個程式即實作一個最簡單的代理伺服器功能,它將連線來源端的訊息轉接至連線目的端,而連線目的端的訊息轉接至連線來源端,為了簡化程式邏輯,這個程式一次只能服務一個連線:
  • SimpleProxyServer.java
package onlyfun.caterpillar;

import java.io.*;
import java.net.*;

public class SimpleProxyServer {
public static void main(String[] args) {
String host; // 代理的對象主機
int remotePort; // 代理對象連接埠
int localPort; // 本機連接埠
BufferedReader reader;

try {
reader = new BufferedReader(
new InputStreamReader(System.in));

System.out.println("SimpleProxyServer v0.1");
System.out.print("代理的對象主機: ");
host = reader.readLine();
System.out.print("代理對象連接埠: ");
remotePort = Integer.parseInt(reader.readLine());
System.out.print("本機連接埠: ");
localPort = Integer.parseInt(reader.readLine());

runServer(host, remotePort, localPort);
}
catch(IOException e) {
System.err.println(e.toString());
}
}

public static void runServer(String host, int remotePort, int localPort) {
try {
System.out.printf("Proxy伺服器啟動...Port %d%n", localPort);
ServerSocket proxyServerSkt = new ServerSocket(localPort);
System.out.println("OK!");

while(true) {
System.out.println("傾聽客戶端.....");
Socket clientSkt = proxyServerSkt.accept();
System.out.printf("%s 連線..%n",
clientSkt.getInetAddress().toString());

// 客戶端的來往訊息
final BufferedInputStream clientInputStream =
new BufferedInputStream(clientSkt.getInputStream());
PrintStream clientPrintStream =
new PrintStream(clientSkt.getOutputStream());

// 伺服端的來往訊息
final Socket serverSkt = new Socket(host, remotePort);
BufferedInputStream fromServerMsg = new BufferedInputStream(
serverSkt.getInputStream());
final PrintStream serverPrintStream = new
PrintStream(serverSkt.getOutputStream());

// 由客戶端至伺服器的訊息溝通執行緒
Thread client = new Thread() {
public void run() {
int read;
try {
while((read = clientInputStream.read()) != -1) {
serverPrintStream.write(read);
serverPrintStream.flush();
}
}
catch(IOException e) {}

// 中斷至伺服器的連線
try {
serverSkt.close();
}
catch(IOException e) {
System.err.println(e.toString());
}
}
};

client.start();

// 主執行緒為由伺服器至客戶端的訊息
int read;
try {
while((read = fromServerMsg.read()) != -1) {
clientPrintStream.write(read);
clientPrintStream.flush();
}
}
catch(IOException e) {
e.printStackTrace();
};

// 中斷與客戶端的連線
try {
clientSkt.close();
}
catch(IOException e) {
e.printStackTrace();
}
}
}
catch(UnknownHostException e) {
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
}
}

您有幾個方法可以測試這個程式,首先啟動程式:
SimpleProxyServer v0.1
代理的對象主機: ptt.cc
代理對象連接埠: 23
本機連接埠: 23
Proxy伺服器啟動...Port 23..OK!
傾聽客戶端.....

範例在本機8888連接埠啟動了代理伺服器,代理的對象是ptt.cc,接下來您可以直接telnet localhost 23,本機所啟動的代理伺服器會幫您連線至ptt.cc,接下來的一切操作就與BBS操作相同了。

您也可以讓它代理一個Web伺服器,假設本機代理伺服器連接埠也設定為8080,當使用瀏覽器鍵入http://localhost:8080/時,您會發現瀏覽器自動連接至代理的網頁伺服器。