JabberServer 雖能運作,但每次只能服務一個客戶端。典型伺服器需同時處理多個客戶請求,關鍵在於多執行緒機制。對不支援多執行緒的語言,這極其困難;但 Java 對多執行緒處理已盡可能簡化,讓伺服器控制多個客戶並非難事。
最基本做法:伺服器建立單一 ServerSocket,呼叫 accept() 等待新連線。一旦 accept() 回傳,取得 Socket,用它新建一個執行緒,專門服務該特定客戶。然後再次呼叫 accept() 等待下一個連線請求。
//: MultiJabberServer.java
import java.io.*;
import java.net.*;
class ServeOneJabber extends Thread {
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public ServeOneJabber(Socket s) throws IOException {
socket = s;
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
start(); // 呼叫 run()
}
public void run() {
try {
while (true) {
String str = in.readLine();
if (str.equals("END")) break;
System.out.println("Echoing: " + str);
out.println(str);
}
System.out.println("closing...");
} catch (IOException e) { }
finally {
try { socket.close(); } catch(IOException e) {}
}
}
}
public class MultiJabberServer {
static final int PORT = 8080;
public static void main(String[] args) throws IOException {
ServerSocket s = new ServerSocket(PORT);
System.out.println("Server Started");
try {
while(true) {
Socket socket = s.accept(); // 阻塞直到連線
try {
new ServeOneJabber(socket);
} catch(IOException e) {
socket.close(); // 建構子失敗時關閉 socket
}
}
} finally {
s.close();
}
}
} ///:~
start(),完成執行緒初始化並調用 run()Socket 在 ServeOneJabber 外部建立,清理責任「共享」。建構子失敗拋出例外由呼叫者清理;成功則由執行緒在 run() 的 finally 區塊關閉MultiJabberServer 僅負責 accept() 並將 Socket 傳給建構子,連線中斷後執行緒自然消失為驗證伺服器能同時服務多客戶,下列程式建立多個客戶執行緒連接同一伺服器。每個執行緒「壽命」有限,到期釋放空間給新執行緒。最大執行緒數由 MAX_THREADS 控制——設太大可能耗盡資源導致不可預期錯誤。
//: MultiJabberClient.java
class JabberClientThread extends Thread {
private Socket socket;
private BufferedReader in;
private PrintWriter out;
private static int counter = 0;
private int id = counter++;
private static int threadcount = 0;
// ... 建構子建立連線、啟動執行緒
public void run() {
try {
for(int i = 0; i < 25; i++) {
out.println("Client " + id + ": " + i);
String str = in.readLine();
System.out.println(str);
}
out.println("END");
} catch(IOException e) { }
finally {
try { socket.close(); } catch(IOException e) {}
threadcount--;
}
}
}
public class MultiJabberClient {
static final int MAX_THREADS = 40;
public static void main(String[] args) throws IOException, InterruptedException {
InetAddress addr = InetAddress.getByName(null);
while(true) {
if(JabberClientThread.threadCount() < MAX_THREADS)
new JabberClientThread(addr);
Thread.currentThread().sleep(100);
}
}
} ///:~
threadcount 追蹤目前存在的 JabberClientThread 數量,建構子遞增,run() 結束時遞減。main() 中檢查執行緒數量,過多則暫不建立,進入睡眠。可嘗試逐漸增大 MAX_THREADS,觀察系統資源降至危險程度的臨界點。