首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 網路程式設計實戰:基於 Socket 的 Client/Server 通訊完整指南

     編輯:Java 相關知識

前言:Java 網路編程核心優勢

Java 誕生即為網路而生:java.net 封裝 TCP/IP、java.io 統一流模型、多執行緒原生支援、跨平台位元組碼。本文將從 Socket 機制、流處理、多執行緒 Server、Applet/Application 通訊、中文編碼解決方案、除錯心得完整實戰。

核心基礎:Socket 與 I/O 流

Socket 通訊模型

Socket 機制示意圖
  • ServerSocket:伺服器端監聽埠,accept() 封鎖等待客戶端連線,回傳 Socket
  • Socket:雙向通訊端點,getInputStream()/getOutputStream() 取得流
  • TCP 提供可靠、有序、全雙工位元組流

資料流選擇:為什麼用 DataInputStream/DataOutputStream?

關鍵優勢: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 關鍵步驟

類別實作介面關鍵方法適用場景
DataInputStreamDataInputreadUTF(), readInt(), readLong(), readFully()跨平台基本型別讀取
DataOutputStreamDataOutputwriteUTF(), writeInt(), writeLong()跨平台基本型別寫入
項目ApplicationApplet
進入點main()init() (瀏覽器自動呼叫)
繼承類別Frame/JFrameApplet/JApplet
標題列setTitle()移除 (由 HTML 控制)
佈局管理預設 BorderLayout在 init() 明確設定
選單MenuBar改用 Button/MenuItem
部署獨立執行HTML + <applet> 標籤

實戰除錯心得總結

  1. 埠號一致性:Client/Server 必須使用相同埠號 (範例 1111)
  2. 中文編碼:棄用 `PrintStream`/`BufferedReader`,改用 `DataOutputStream.writeUTF` / `DataInputStream.readUTF` 徹底解決亂碼
  3. 埠號佔用:程式未正確關閉 `ServerSocket` 導致 `BindException`,確保 `finally` 區塊釋放資源
  4. Applet 安全沙箱:未簽名 Applet 僅能連接原始碼伺服器 (Codebase),跨主機需簽名憑證 (參考文章 131401)
  5. 自訂類別引用: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 核心模型在微服務、即時通訊、物聯網閘道中依然是基石技術。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved