| 類別 | 實作介面 | 關鍵方法 | 適用場景 |
| DataInputStream | DataInput | readUTF(), readInt(), readLong(), readFully() | 跨平台基本型別讀取 |
| DataOutputStream | DataOutput | writeUTF(), writeInt(), writeLong() | 跨平台基本型別寫入 |
關鍵優勢:writeUTF/readUTF 採用修改版 UTF-8 編碼,**完美支援中文**,避免 `PrintStream`/`BufferedReader` 預設平台編碼導致的亂碼。
完整實作:多執行緒 Server + Applet Client
Server 端:多執行緒回聲伺服器 (附帶業務邏輯鉤子)
// S.java
import java.io.*;
import java.net.*;
class ThreadEchoHandler extends Thread {
private Socket incoming;
private int counter;
private BusinessLogic biz = new BusinessLogic(); // 自訂業務類別
ThreadEchoHandler(Socket socket, int id) {
this.incoming = socket; this.counter = id;
}
public void run() {
try (DataInputStream in = new DataInputStream(incoming.getInputStream());
DataOutputStream out = new DataOutputStream(incoming.getOutputStream())) {
System.out.println("Client #" + counter + " connected");
String request;
while ((request = in.readUTF()) != null) {
System.out.println("Received: " + request);
// 業務處理鉤子
biz.process(request);
String response = biz.getResult();
if (!"null".equals(response)) { // 非查詢類指令不回應
out.writeUTF(response);
out.flush();
}
}
} catch (IOException e) {
System.err.println("Client #" + counter + " error: " + e);
} finally {
try { incoming.close(); } catch (IOException ignored) {}
}
}
}
public class S {
public static void main(String[] args) {
try (ServerSocket server = new ServerSocket(1111)) {
System.out.println("Server started on port 1111");
int clientId = 1;
while (true) {
Socket client = server.accept();
new ThreadEchoHandler(client, clientId++).start();
}
} catch (IOException e) {
System.err.println("Server fatal: " + e);
}
}
}
// 簡易業務邏輯範例
class BusinessLogic {
private String result = "null";
public void process(String cmd) {
// 實際解碼、資料庫操作、監控統計...
if (cmd.startsWith("QUERY")) result = "DB_RESULT_" + cmd;
else result = "null";
}
public String getResult() { return result; }
}
Client 端:Applet 通訊元件
// Echo.java (Applet)
import java.applet.Applet;
import java.awt.*;
import java.io.*;
import java.net.*;
public class Echo extends Applet {
private TextArea display = new TextArea(5, 80);
private Socket socket;
private DataOutputStream out;
private DataInputStream in;
public void init() {
setBackground(Color.white);
display.setEditable(false);
add(display);
try {
// 連線伺服器 (Applet 只能連回來源主機,除非簽名)
socket = new Socket("10.102.4.41", 1111);
out = new DataOutputStream(socket.getOutputStream());
in = new DataInputStream(socket.getInputStream());
display.appendText("Connected to server\n");
} catch (IOException e) {
display.appendText("Connection failed: " + e + "\n");
}
}
// 供其他 Applet 呼叫的發送方法
public void send(String msg) {
try {
out.writeUTF(msg);
out.flush();
} catch (IOException e) {
display.appendText("Send error: " + e + "\n");
}
}
// 供其他 Applet 呼叫的接收方法
public String receive() {
try {
String msg = in.readUTF();
display.appendText("Received: " + msg + "\n");
return msg;
} catch (IOException e) {
display.appendText("Receive error: " + e + "\n");
return null;
}
}
}
HTML 頁面嵌入 Applet
<html>
<body>
<applet code="Echo.class" width=400 height=200 name="commApplet"></applet>
<applet code="BusinessApplet.class" width=400 height=300></applet>
</body></html>
Applet 間通訊:共享通訊 Applet
// BusinessApplet.java
public class BusinessApplet extends Applet {
private Echo commApplet;
public void init() {
// 透過名稱取得共享通訊 Applet 實例
commApplet = (Echo) getAppletContext().getApplet("commApplet");
}
public void doBusiness() {
commApplet.send("QUERY_USER_123");
String reply = commApplet.receive();
// 處理回應...
}
}
Application 轉 Applet 關鍵步驟
| 項目 | Application | Applet |
| 進入點 | main() | init() (瀏覽器自動呼叫) |
| 繼承類別 | Frame/JFrame | Applet/JApplet |
| 標題列 | setTitle() | 移除 (由 HTML 控制) |
| 佈局管理 | 預設 BorderLayout | 在 init() 明確設定 |
| 選單 | MenuBar | 改用 Button/MenuItem |
| 部署 | 獨立執行 | HTML + <applet> 標籤 |
實戰除錯心得總結
- 埠號一致性:Client/Server 必須使用相同埠號 (範例 1111)
- 中文編碼:棄用 `PrintStream`/`BufferedReader`,改用 `DataOutputStream.writeUTF` / `DataInputStream.readUTF` 徹底解決亂碼
- 埠號佔用:程式未正確關閉 `ServerSocket` 導致 `BindException`,確保 `finally` 區塊釋放資源
- Applet 安全沙箱:未簽名 Applet 僅能連接原始碼伺服器 (Codebase),跨主機需簽名憑證 (參考文章 131401)
- 自訂類別引用:
import 套件.類別; → 類別 實例 = new 類別(); → 實例.方法()
現代替代方案提示
Applet 已被瀏覽器淘汰 (Chrome 45+、Firefox 52+ 移除 NPAPI)。現代架構建議:
- WebSocket:全雙工長連線,JSR 356 / Spring WebSocket
- REST/HTTP + JSON:通用、防火牆友善、易除錯
- gRPC:高效能 RPC,HTTP/2 + Protobuf
- Server-Sent Events (SSE):單向伺服器推送
總結
Java Socket 編程以「流」為抽象,配合多執行緒實現高併發 Server。掌握 `DataInput/OutputStream` 的跨平台序列化、`ServerSocket.accept()` 迴圈、`Thread` 隔離客戶端邏輯、Applet 間 `getAppletContext()` 共享實例,即可構建健壯的 C/S 通訊系統。雖然 Applet 退出歷史舞臺,但 Socket 核心模型在微服務、即時通訊、物聯網閘道中依然是基石技術。
- 上一篇文章: Java 中的 SOAP 技術實戰:從原理到 HelloWorld 完整部署
- 下一篇文章: Java 入門經典:從 Hello World 到圖形介面的完整實作指南
Copyright ©
程式碼之家 All Rights Reserved