Posted on 2009-12-13 15:45
啥都寫點 閱讀(261)
評論(0) 編輯 收藏 所屬分類:
J2SE
本實例實現(xiàn)了一個代理服務器,實質上是實現(xiàn)了一個代理服務,部署在前文實現(xiàn)的通用服務器框架上。用戶需要指定目標服務器的IP地址和端口,然后在瀏覽器中輸入本地網(wǎng)址,便可以訪問目標服務器上的網(wǎng)頁。

代理服務作為一個服務部署在服務器框架上,必須實現(xiàn)book.net.GeneraServer$Service接口,實現(xiàn)它的serve方法。

當客戶端請求代理服務時,客戶端與代理服務之間會建立socket,代理服務收到請求后,會與目標服務器建立Socket。

代理服務持有兩個Socket,它起中轉作用,將來自客戶端的請求轉發(fā)給服務器,來自服務器端的響應消息轉發(fā)給客戶端,可以通過兩個線程分別處理請求消息的轉發(fā)和響應消息的轉發(fā)。

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


/** *//**
* 本類為支持多線程服務器框架的GeneralServer類提供了一個相關的服務Service,
* 使GeneralServer提供代理服務。內部類ProxyService實現(xiàn)了GeneralServer.Service接口。
**/

public class ProxyServer
{
// 命令行的格式是:代理服務器連接的目標服務器的域名、端口以及代理服務開啟的本地端口
public static final String usage =
"Usage: java book.net.ProxyServer <remotehost> <remoteport> <localport>
";

/** *//**
* 創(chuàng)建一個GeneralServer對象,并添加代理服務ProxyService對象到這個服務器上。
**/

public static void main(String[] args)
{

try
{
// 檢驗參數(shù)

if ((args.length == 0) || (args.length % 3 != 0))
{
throw new IllegalArgumentException("Wrong number of args");
}
// 創(chuàng)建一個服務器:GeneralServer對象,用于管理服務,
// 第一個參數(shù)為null表示服務器的日志輸出在標準輸出上,第二個參數(shù)12表示最大連接數(shù)
GeneralServer server = new GeneralServer(null, 12);
// 從命令行中解析參數(shù),可以創(chuàng)建多個代理服務
int i = 0;

while(i < args.length)
{
// 目標服務器的域名和地址
String remotehost = args[i++];
int remoteport = Integer.parseInt(args[i++]);
// 代理服務在本地的端口
int localport = Integer.parseInt(args[i++]);
// 創(chuàng)建一個連接到目標服務器的代理服務對象,并部署在本地服務器上的localport端口上。
server.addService(new ProxyService(remotehost, remoteport), localport);
}
}

catch (Exception e)
{
System.err.println(e);
System.err.println(usage);
System.exit(1);
}
}

/** *//**
* 實現(xiàn)代理服務的功能,實現(xiàn)了GeneralServer.Service接口的serve方法。
* 首先與目標服務器建立連接,然后將來自客戶端的輸入流輸出到目標服務器端,
* 將來自目標服務器的輸入流輸出到客戶端。
* 輸入輸出流的流向控制是通過2個線程實現(xiàn)的。
*/

public static class ProxyService implements GeneralServer.Service
{
// 目標服務器的域名和端口
String remotehost;
int remoteport;

/** *//**
* 構造方法
*/

public ProxyService(String host, int port)
{
this.remotehost = host;
this.remoteport = port;
}

/** *//**
* 當客戶端調用GeneralServer上的代理服務時,將調用serve方法
*/

public void serve(InputStream in, OutputStream out)
{
// 來自客戶端的輸入流
final InputStream from_client = in;
// 輸出到客戶端的輸出流
final OutputStream to_client = out;
// 與目標服務器的輸入輸出流
final InputStream from_server;
final OutputStream to_server;

// 連接到目標服務器的socket對象
final Socket server;

try
{
// 連接目標服務器,初始化from_server,to_server流
server = new Socket(remotehost, remoteport);
from_server = server.getInputStream();
to_server = server.getOutputStream();

} catch (Exception e)
{
// 如果連接失敗,則向客戶端發(fā)送失敗的消息
PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
pw.print("Proxy server could not connect to " + remotehost
+ ":" + remoteport + "\n");
pw.flush();
pw.close();

try
{
in.close();

} catch (IOException ex)
{
}
return;
}
// 創(chuàng)建一個線程,將來自客戶端的輸入流字節(jié)輸出到目標服務器上

Thread client2server = new Thread()
{

public void run()
{
byte[] buffer = new byte[2048];
int bytes_read;

try
{
// 從來自客戶端的輸入流中讀取數(shù)據(jù),寫到目標服務器上

while ((bytes_read = from_client.read(buffer)) != -1)
{
to_server.write(buffer, 0, bytes_read);
to_server.flush();
}

} catch (IOException e)
{

} finally
{
// 當線程結束時,關閉與目標服務器的socket連接和2個輸入輸出流

try
{
server.close();
to_client.close();
from_client.close();

} catch (IOException e)
{
}
}
}
};

// 創(chuàng)建一個線程,用于將來自目標服務器的輸入流輸出到客戶端

Thread server2client = new Thread()
{

public void run()
{
byte[] buffer = new byte[2048];
int bytes_read;

try
{
// 讀取來自目標服務器的輸入流from_server,然后輸出到to_client

while ((bytes_read = from_server.read(buffer)) != -1)
{
to_client.write(buffer, 0, bytes_read);
to_client.flush();
}

} catch (IOException e)
{

} finally
{

try
{
server.close();
to_client.close();
from_client.close();

} catch (IOException e)
{
}
}
}
};

// 啟動這兩個線程
client2server.start();
server2client.start();

// 當這個兩個線程運行結束時,本次服務才結束

try
{
client2server.join();
server2client.join();

} catch (InterruptedException e)
{
}
}
}
}

在命令行下輸入"java book.net.ProxyServer
www.google.com 80 5555",將啟動代理服務器,端口為5555.打開瀏覽器,在地址欄中輸入'
http://localhost:5555 " 將會打開
www.google.com的主頁。
--
學海無涯