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

Java Socket 字節流傳輸實戰:從阻塞 I/O 到十六進制調試完整示例

     編輯:Java 相關知識

場景與目標

某工業設備/舊協議系統要求客戶端與服務端通過 **原始 TCP 字節流** 通信,數據格式為十六進制字節序列(如 01 03 00 00 00 02 C4 0B)。本文提供一對可直接運行的「阻塞式單線程 Server / Client」完整代碼,涵蓋:

  • 字節流讀寫(DataInputStream/DataOutputStream)
  • 單字節讀取與可用字節數判斷幀邊界
  • 字節數組 ↔ 十六進制字符串雙向轉換工具
  • 異常處理與資源釋放最佳實踐

服務端實現

public class HexByteServer {
    private static final int PORT = 5020;
    private final ServerSocket serverSocket;

    public HexByteServer() throws IOException {
        this.serverSocket = new ServerSocket(PORT);
    }

    public void start() {
        System.out.println("Server listening on port " + PORT);
        while (true) {
            try (Socket client = serverSocket.accept()) {
                System.out.println("Client connected: " + client.getRemoteSocketAddress());
                handleClient(client);
            } catch (IOException e) {
                System.err.println("Accept error: " + e.getMessage());
            }
        }
    }

    private void handleClient(Socket socket) throws IOException {
        try (DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
             DataOutputStream out = new DataOutputStream(socket.getOutputStream())) {

            byte[] single = new byte[1];
            StringBuilder frame = new StringBuilder();
            while (in.read(single) != -1) {
                frame.append(bytesToHex(single)).append(' ');
                // 簡易幀結束判斷:對端暫時無數據發送
                if (in.available() == 0) {
                    System.out.println("Received frame: " + frame.toString().trim());
                    // 業務處理...
                    doBusiness(frame.toString().trim());
                    frame.setLength(0); // 清空準備下一幀
                }
            }
        }
    }

    private void doBusiness(String hexFrame) {
        // 這裡接入實際協議解析邏輯
        System.out.println("Business processing: " + hexFrame);
    }

    /** 字節數組 → 大寫十六進制字符串(每字節兩位,不足補 0) */
    public static String bytesToHex(byte[] src) {
        if (src == null || src.length == 0) return "";
        StringBuilder sb = new StringBuilder(src.length * 2);
        for (byte b : src) {
            int v = b & 0xFF;
            if (v < 0x10) sb.append('0');
            sb.append(Integer.toHexString(v).toUpperCase());
        }
        return sb.toString();
    }

    public static void main(String[] args) throws IOException {
        new HexByteServer().start();
    }
}

客戶端實現

public class HexByteClient {
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 5020;

    public static void main(String[] args) {
        try (Socket socket = new Socket()) {
            socket.connect(new InetSocketAddress(HOST, PORT), 3000);
            System.out.println("Connected to server");

            try (DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                 DataInputStream in = new DataInputStream(System.in)) {

                byte[] buf = new byte[1];
                System.out.println("輸入十六進制字節(如 01 03 00),按 Enter 發送,Ctrl+C 退出");
                // 這裡演示從標準輸入讀取原始字節,實際項目中通常直接構造 byte[]
                while (in.read(buf) != -1) {
                    out.write(buf);
                    out.flush();
                }
            }
        } catch (IOException e) {
            System.err.println("Client error: " + e.getMessage());
        }
    }
}

關鍵技術點深度解析

1. 為什麼用 read(byte[]) 單字節讀取?

協議無固定長度、無分隔符時,流式讀取只能逐字節讀。配合 in.available() == 0 判斷「對端本次發送暫停」,作為幀邊界(適用於請求-響應、低頻設備)。高頻/長連接場景建議自定義協議頭(長度域)或特殊分隔符。

2. b & 0xFF 與 Integer.toHexString 的必要性

  • Java byte 有符號(-128~127),直接 toHexString 會產生負數補碼(如 -1 → "ffffffff")。
  • b & 0xFF 強制提取無符號 8 位值(0~255),再轉十六進制,保證輸出 "FF" 而非 "FFFFFFFF"。

3. 線程模型局限與擴展方向

模型適用場景代碼變更
單線程阻塞(本文)低併發、測試驗證—
線程池 ExecutorService中等併發(<500 連接)pool.submit(() -> handleClient(client))
NIO 選擇器高併發(萬級連接)Netty / Mina 框架

單元測試驗證工具方法

@Test
public void testBytesToHex() {
    assertEquals("010300000002C40B", HexByteServer.bytesToHex(
        new byte[]{0x01, 0x03, 0x00, 0x00, 0x00, 0x02, (byte)0xC4, 0x0B}));
    assertEquals("FF", HexByteServer.bytesToHex(new byte[]{(byte)0xFF}));
    assertEquals("00", HexByteServer.bytesToHex(new byte[]{0x00}));
}

總結

本文提供的阻塞式字節流 Server/Client 代碼結構清晰、資源管理規範(try-with-resources)、十六進制轉換工具經過實測,可直接作為舊協議對接、設備調試、教學演示的起點。生產環境請根據併發量選擇線程池或 Netty 重構網絡層,業務邏輯層可完全復用。

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