---- 隨 著Java 語 言 的 日 益 流 行, 特 別 是Java 與Internet Web 的 密 切 結 合, 使 它 在 全 球 取 得 了 巨 大 的 成 功。Java 語 言 以 其 獨 立 於 平 台、 面 向 對 象、 分 布 式、 多 線 索 及 完 善 的 安 全 機 制 等 特 色, 成 為 現 代 信 息 系 統 建 設 中 的 良 好 的 開 發 平 台 和 運 行 環 境。
一、Java 網 絡 應 用 模 型
---- 和Internet 上 的 許 多 環 境 一 樣, 完 整 的Java 應 用 環 境 實 際 上 也 是 一 個 客 戶 機/ 服 務 器 環 境, 更 確 切 地 說 是 浏 覽 器/ 服 務 器 模 型( 即Browser/Server 模 型, 簡 稱Web 模 型)。 但 與 傳 統 的 客 戶 機/ 服 務 器(C/S) 的 二 層 結 構 不 同, 應 用Java 的Web 模 型 是 由 三 層 結 構 組 成 的。 傳 統 的C/S 結 構 通 過 消 息 傳 遞 機 制, 由 客 戶 端 發 出 請 求 給 服 務 器, 服 務 器 進 行 相 應 處 理 後 經 傳 遞 機 制 送 回 客 戶 端。 而 在Web 模 型 中, 服 務 器 一 端 被 分 解 成 兩 部 分: 一 部 分 是 應 用 服 務 器(Web 服 務 器), 另 一 部 分 是 數 據 庫 服 務 器,Java 網 絡 應 用 結 構 模 型 如 圖 所 示:
---- HTTP
---- TCP/IP
Java 網 絡 應 用 模 型
---- 針 對 分 布 式 計 算 環 境,Java 通 過 其 網 絡 類 庫 提 供 了 良 好 的 支 持。 對 數 據 分 布,Java 提 供 了 一 個URL(Uniform Resource Locator) 對 象, 利 用 此 對 象 可 打 開 並 訪 問 網 絡 上 的 對 象, 其 訪 問 方 式 與 訪 問 本 地 文 件 系 統 幾 乎 完 全 相 同。 對 操 作 分 布,Java 的 客 戶 機/ 服 務 器 模 式 可 以 把 運 算 從 服 務 器 分 散 到 客 戶 一 端( 服 務 器 負 責 提 供 查 詢 結 果, 客 戶 機 負 責 組 織 結 果 的 顯 示), 從 而 提 高 整 個 系 統 的 執 行 效 率, 增 加 動 態 可 擴 充 性。Java 網 絡 類 庫 是Java 語 言 為 適 應Internet 環 境 而 進 行 的 擴 展。 另 外, 為 適 應Internet 的 不 斷 發 展,Java 還 提 供 了 動 態 擴 充 協 議, 以 不 斷 擴 充Java 網 絡 類 庫。
---- Java 的 網 絡 類 庫 支 持 多 種Internet 協 議, 包 括Telnet, FTP 和HTTP(WWW), 與 此 相 對 應 的Java 網 絡 類 庫 的 子 類 庫 為:
Java.Net
Java.Net.FTP
Java.Net.www.content
Java.Net.www.Html
Java.Net.www.http
---- 這 些 子 類 庫 各 自 容 納 了 可 用 於 處 理Internet 協 議 的 類 和 方 法。 其 中,java.net 用 於 處 理 一 些 基 本 的 網 絡 功 能, 包 括 遠 程 登 錄(Telnet);java.net.ftp 用 於 處 理FTP 協 議;java.net.www.content 用 於 處 理WWW 頁 面 內 容;java.net.www.Html 和Java.Net.www.http 則 分 別 提 供 了 對Html 語 言 和HTTP 協 議 的 支 持。
二、 客 戶 機/ 服 務 器 環 境 下 的Java 應 用 程 序
---- 客 戶 機/ 服 務 器 在 分 布 處 理 過 程 中, 使 用 基 於 連 接 的 網 絡 通 信 模 式。 該 通 信 模 式 首 先 在 客 戶 機 和 服 務 器 之 間 定 義 一 套 通 信 協 議, 並 創 建 一Socket 類, 利 用 這 個 類 建 立 一 條 可 靠 的 鏈 接; 然 後, 客 戶 機/ 服 務 器 再 在 這 條 鏈 接 上 可 靠 地 傳 輸 數 據。 客 戶 機 發 出 請 求, 服 務 器 監 聽 來 自 客 戶 機 的 請 求, 並 為 客 戶 機 提 供 響 應 服 務。 這 就 是 典 型 的“ 請 求-- 應 答” 模 式。 下 面 是 客 戶 機/ 服 務 器 的 一 個 典 型 運 作 過 程:
---- 1 . 服 務 器 監 聽 相 應 端 口 的 輸 入;
---- 2 . 客 戶 機 發 出 一 個 請 求;
---- 3 . 服 務 器 接 收 到 此 請 求;
---- 4 . 服 務 器 處 理 這 個 請 求, 並 把 結 果 返 回 給 客 戶 機;
---- 5 . 重 復 上 述 過 程, 直 至 完 成 一 次 會 話 過 程。
---- 按 照 以 上 過 程, 我 們 使 用Java 語 言 編 寫 一 個 分 別 針 對 服 務 器 和 客 戶 機 的 應 用 程 序(Application)。 該 程 序 在 服 務 器 上 時, 程 序 負 責 監 聽 客 戶 機 請 求, 為 每 個 客 戶 機 請 求 建 立Socket 連 接, 從 而 為 客 戶 機 提 供 服 務。 本 程 序 提 供 的 服 務 為: 讀 取 來 自 客 戶 機 的 一 行 文 本, 反 轉 該 文 本, 並 把 它 發 回 給 客 戶 機。
---- 通 過 該 程 序 實 例 我 們 看 到, 使 用Java 語 言 設 計C/S 程 序 時 需 要 注 意 以 下 幾 點:
---- (1)、 服 務 器 應 使 用ServerSocket 類 來 處 理 客 戶 機 的 連 接 請 求。 當 客 戶 機 連 接 到 服 務 器 所 監 聽 的 端 口 時,ServerSocket 將 分 配 一 新 的Socket 對 象。 這 個 新 的Socket 對 象 將 連 接 到 一 些 新 端 口, 負 責 處 理 與 之 相 對 應 客 戶 機 的 通 信。 然 後, 服 務 器 繼 續 監 聽ServerSocket, 處 理 新 的 客 戶 機 連 接。
---- Socket 和ServerSocket 是Java 網 絡 類 庫 提 供 的 兩 個 類。
---- (2)、 服 務 器 使 用 了 多 線 程 機 制。Server 對 象 本 身 就 是 一 個 線 程, 它 的run() 方 法 是 一 個 無 限 循 環, 用 以 監 聽 來 自 客 戶 機 的 連 接。 每 當 有 一 個 新 的 客 戶 機 連 接 時,ServerSocket 就 會 創 建 一 個 新 的Socket 類 實 例, 同 時 服 務 器 也 將 創 建 一 新 線 程, 即 一 個Connection 對 象, 以 處 理 基 於Socket 的 通 信。 與 客 戶 機 的 所 有 通 信 均 由 這 個Connection 對 象 處 理。Connection 的 構 造 函 數 將 初 始 化 基 於Socket 對 象 的 通 信 流, 並 啟 動 線 程 的 運 行。 與 客 戶 機 的 通 信 以 及 服 務 的 提 供, 均 由Connection 對 象 處 理。
---- (3)、 客 戶 機 首 先 創 建 一Socket 對 象, 用 以 與 服 務 器 通 信。 之 後 需 創 建 兩 個 對 象:DataInputStream 和PrintStream, 前 者 用 以 從Socket 的InputStream 輸 入 流 中 讀 取 數 據, 後 者 則 用 於 往Socket 的OutputStream 中 寫 數 據。 最 後, 客 戶 機 程 序 從 標 准 輸 入( 如: 控 制 台) 中 讀 取 數 據, 並 把 這 些 數 據 寫 到 服 務 器, 在 從 服 務 器 讀 取 應 答 消 息, 然 後 大 這 些 應 答 消 息 寫 到 到 准 輸 出。
---- 以 下 本 別 為 服 務 器 和 客 戶 機 端 的 源 程 序 清 單。 本 程 序 在NT 4.0 網 絡 環 境(TCP/IP) 下 使 用JDK1.1 調 試 通 過。
---- 1 . 編 寫 服 務 器 類Java 程 序
// Server.Java
import Java.io.*;
import Java.Net.*;
public class Server extends Thread
{
public final static int Default_Port=6543;
protectd int port;
protectd ServerSockrt listen_socket;
//定義出錯例程:
如果出現異常錯誤,退出程序。
Public static void fail(Exception e, String msg)
{
System.err.println(msg + ": " + e);
System.exit(1);
}
//定義並啟動服務器的Socket例程,
監聽客戶機的連接請求。
public Server(int port)
{
if(port == 0) port = Default_Port;
this.port = port;
try
{
listen_socket = new ServerSocket(port);
}
catch(IOException e) fail(e,
"Exception creating server socket");
System.out.println("Server:
listening on port" + port);
This.start();
}
---- /* 下 面 為 服 務 器 監 聽 線 程 的 主 程 序。 該 線 程 一 直 循 環 執 行, 監 聽 並 接 受 客 戶 機 發 出 的 連 接 請 求。 對 每 一 個 連 接, 均 產 生 一 個 連 接 對 象 與 之 對 應, 通 過Socket 通 道 進 行 通 信。
*/
public void run()
{
try
{
while(true)
{
Socket clIEnt_socket = listen_socket.accept();
Connection c = new Connection(clIEnt_socket);
}
}
catch(IOException e) fail
(e,"Exception while listening for connections")
}
//啟動服務器主程序
public static void main(String args[])
{
int port = 0;
if (args.length == 1)
{
try port = Integer.parseInt(args[0]);
catch(NumberFormatException e) port = 0;
}
new Server(port);
}// End of the main
}// End of Server class
//以下定義了Connection類,
它是用來處理與客戶機的所有通信的線程。
class Connection extends Thread
{
protected Socket clIEnt;
protected DataInputStream in;
protected PrintStream out;
//初始化通信流並啟動線程
public Connection(Socket clIEnt_socket)
{
client = clIEnt_socket;
try
{
in = new DataInputStream(clIEnt.getinputStream());
out = new PrintStream(clIEnt.getOutputStream());
}
catch(IOException e)
{
try clIEnt.close();
catch(IOException e2);
System.err.println
("Exception while getting socket streram: " + e);
Return;
}
this.start;
}// End of Connection method
//服務例程:讀出一行文本;
反轉文本;返回文本。
public void run()
{
String line;
StringBuffer revline;
int len;
try
{
for(;;)
{
// Read a line
line = in.readline();
if(line == null) break;
// Reverse the line
len = line.length();
revline = new StringBuffer(len);
for(int i = len-1; i >=0; i--)
revline.insert(len-1-I;line.charAt(i));
// Write out the reverse line
out.println(revline);
}
catch(IOException e);
finally try clIEnt.close();
catch(IOException e2);
}// End of run method
}// End of Connection class
2.編寫客戶機類Java程序
// ClIEnt.Java
importJava.io.*;
importJava.Net.*;
public class ClIEnt extends
{
public static final int Default_Port = 6543;
//定義出錯例程
public static final void usage()
{
System.out.println("Usage:
Java ClIEnt []");
System.exit(0);
}
public static void main(String args[])
{
int port = Default_Port;
Socket s = null;
//解析端口參數
if ((args.length != 1)&&
(args.length != 2 )) usage();
if (args.length == 1)
port = Default_Port;
else
{
try port = Integer.parseInt(args[1]);
catch(NumberFormaatException e) usage();
}
try{
//產生一個Socket,
通過指定的端口與主機通信。
s = new Socket(args[0], port);
//產生用於發出和接收的文本字符流
DataInputStream sin = new
DataInputStream(s.getInputStream());
PrintStream sout = new
DataInputStream(s.getInputStream());
//從控制台讀入字符流
DataInputStream in = new
DataInputStream(System.in);
//返回連接的地址和端口
System.out.println("Connected
to"+s.getInetAddress()
+":"+ s.getPort());
String line;
For(;;)
{
//顯示提示符
System.out.print(" >");
System.out.flush();
//讀入控制台輸入的一行字符
line = in.readline();
if (line == null) break;
//將接收的文本行送至服務器
sout.println(line);
//從服務器接收一行字符
line = sin.readline();
// Check if connection is
closed(i.e. for EOF)
if(line == null)
{
System.out.println("Connection closed by server.");
Break;
}
//在控制台上顯示接收的字符
System.out.println(line);
}// End of for loop
}// End of try
catch(IOException e ) System.err.println(e);
// Always be sure to close the socket
finally
{
try if(s != null) s.close();
catch(IOException e2);
}
}// End of main
}// End of ClIEnt
---- 運 行 該 客 戶 機 程 序 時, 必 須 以 服 務 器 主 機 名 作 為 第 一 個 參 數, 服 務 器 端 口 號 為 第 二 個 參 數, 其 中 服 務 器 端 口 號 可 缺 省。