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

HttpClient 實戰:客戶端與伺服器端 Java 物件序列化傳輸完整範例

     編輯:Java 程式設計入門

背景與動機

Apache HttpClient 封裝了 java.net 底層細節,提供優雅的流式 API。本文示範如何利用 Java 序列化機制,透過 HTTP POST 在客戶端與 Servlet 伺服器之間雙向傳輸任意 Serializable 物件,並搭配查詢字串傳遞額外參數。

架構概覽

  • 客戶端:將物件序列化為位元組陣列,包裝為 InputStreamRequestEntity 發送;接收回應流並反序列化。
  • 伺服器端:Servlet 從 request.getInputStream() 讀取物件,處理後將結果物件寫入 response.getOutputStream()。
  • 協定:Content-Type 設為 application/octet-stream,查詢字串傳遞簡單參數。

客戶端完整程式碼

import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;
import java.io.*;

public class ObjectTransferClient {

    public static void main(String[] args) throws Exception {
        String url = "https://localhost:8084/system/linantest";
        String queryString = "test=hello";
        String inputObj = " boy!";
        Serializable result = sendObject(url, queryString, inputObj);
        System.out.println("Server returned: " + result);
    }

    public static Serializable sendObject(String url, String queryString, Serializable inputObj) throws IOException {
        HttpClient client = new HttpClient();
        PostMethod post = new PostMethod(url);
        post.setQueryString(queryString);
        post.setRequestHeader("Content-Type", "application/octet-stream");

        ByteArrayOutputStream byteOut = new ByteArrayOutputStream(1024);
        ObjectOutputStream objOut = null;
        ByteArrayInputStream byteIn = null;
        Serializable returnObj = null;

        try {
            // 序列化請求物件
            objOut = new ObjectOutputStream(byteOut);
            objOut.writeObject(inputObj);
            objOut.flush();

            // 發送
            byteIn = new ByteArrayInputStream(byteOut.toByteArray());
            post.setRequestEntity(new InputStreamRequestEntity(byteIn));
            client.executeMethod(post);

            // 接收並反序列化回應
            try (InputStream respStream = post.getResponseBodyAsStream();
                 ObjectInputStream objIn = new ObjectInputStream(respStream)) {
                returnObj = (Serializable) objIn.readObject();
            }
        } catch (ClassNotFoundException e) {
            throw new IOException("Deserialization failed", e);
        } finally {
            // 資源釋放
            closeQuietly(objOut);
            closeQuietly(byteIn);
            post.releaseConnection();
        }
        return returnObj;
    }

    private static void closeQuietly(Closeable c) {
        if (c != null) try { c.close(); } catch (IOException ignored) {}
    }
}

伺服器端 Servlet 完整程式碼

package test.li;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class ObjectTransferServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        processRequest(req, resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        processRequest(req, resp);
    }

    private void processRequest(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String param = req.getParameter("test"); // 取得查詢字串參數

        ObjectInputStream objIn = null;
        ObjectOutputStream objOut = null;

        try {
            // 讀取客戶端傳來的物件
            objIn = new ObjectInputStream(req.getInputStream());
            Object received = objIn.readObject();

            // 業務邏輯:簡單字串拼接示範
            String responseObj = param + received.toString();

            // 序列化回應物件
            resp.setContentType("application/octet-stream");
            objOut = new ObjectOutputStream(resp.getOutputStream());
            objOut.writeObject(responseObj);
            objOut.flush();
        } catch (ClassNotFoundException e) {
            throw new ServletException("Class not found during deserialization", e);
        } finally {
            closeQuietly(objIn);
            closeQuietly(objOut);
        }
    }

    private void closeQuietly(Closeable c) {
        if (c != null) try { c.close(); } catch (IOException ignored) {}
    }
}

關鍵技術點解析

  1. 序列化協定:雙方需共享類別定義(serialVersionUID 一致),否則反序列化會拋 InvalidClassException。
  2. 流處理:使用 ByteArrayOutputStream 緩衝序列化資料,避免直接操作 Socket 輸出流造成半包問題。
  3. 資源管理:finally 區塊確保流與連線釋放,防止連線洩漏導致連線池耗盡。
  4. 擴充性:若需傳輸複雜物件圖,確保所有參考類別皆實作 Serializable;若需版本演進,妥善規劃 serialVersionUID 策略。

安全性與生產環境建議

  • 反序列化風險:Java 原生序列化存在已知漏洞(如 Gadget Chain)。生產環境建議改用 JSON (Jackson/Gson)、Protobuf、Avro 等安全格式。
  • 傳輸加密:務必使用 HTTPS,防止中間人竄改序列化資料流。
  • 輸入驗證:伺服器端應限制可反序列化的類別白名單(如使用 ObjectInputFilter,Java 9+)。
  • 超時設定:客戶端配置連線與讀取超時,避免阻塞線程。

總結

HttpClient 配合 Java 序列化可快速實現物件級 RPC 風格通訊,適合內部可信系統整合。但對於對外服務或高安全要求場景,請採用標準化、語言無關的序列化格式並啟用嚴格的反序列化防護。

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