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

Java Applet 與 Servlet 通信實戰:基於 HTTP 的雙向數據交換

     編輯:Java 相關知識

項目背景

在 B/S 架構早期,Applet 作為富客戶端組件,常需與服務端 Servlet 進行數據交互。本文提供一個完整的「發送消息-接收回顯」示例,演示 Applet 如何通過 URLConnection 發起 HTTP 請求,Servlet 處理後返回響應,實現基礎的客戶端-服務端通信模型。

核心架構

  • 客戶端: 繼承 Applet,實現 ActionListener,構建 GUI(輸入框、發送按鈕、消息顯示區),使用 GridBagLayout 精確佈局。
  • 通信層: 採用 URL + URLConnection 模擬表單提交,關閉緩存,設置輸入流讀取響應。
  • 服務端: 對應 Servlet(此處未給出代碼)接收 message 參數,處理後輸出文本,Applet 讀取並顯示在 messageText 中。

完整客戶端代碼

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;

public class SendApplet extends Applet implements ActionListener {
    // GUI 組件
    Label sendLabel, messageInfo;
    Button sendButton;
    TextField sendText, messageText;

    // 網絡通信
    URLConnection connection;
    String message;
    URL servletBaseURL;

    @Override
    public String getAppletInfo() {
        return "Applet-Servlet Communication Demo";
    }

    @Override
    public synchronized void init() {
        super.init();
        resize(500, 300);

        // 初始化組件
        sendLabel = new Label("Please enter Message");
        messageInfo = new Label("What you have sent:");
        sendText = new TextField(40);
        sendButton = new Button("Send");
        messageText = new TextField(40);
        messageText.setEditable(false);

        // GridBagLayout 精細佈局
        Panel mainPanel = new Panel();
        GridBagLayout gbl = new GridBagLayout();
        GridBagConstraints gbc = new GridBagConstraints();
        mainPanel.setLayout(gbl);

        // 第 0 行:提示標籤
        gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 10; gbc.gridheight = 1;
        gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.NONE;
        gbl.setConstraints(sendLabel, gbc);
        mainPanel.add(sendLabel);

        // 第 1 行:輸入框 + 按鈕
        gbc.gridy = 1; gbc.gridwidth = 9; gbc.fill = GridBagConstraints.HORIZONTAL;
        gbl.setConstraints(sendText, gbc);
        mainPanel.add(sendText);

        gbc.gridx = 9; gbc.gridwidth = 1; gbc.fill = GridBagConstraints.NONE;
        gbl.setConstraints(sendButton, gbc);
        mainPanel.add(sendButton);

        // 第 2 行:回顯標籤
        gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 10;
        gbl.setConstraints(messageInfo, gbc);
        mainPanel.add(messageInfo);

        // 第 3 行:回顯文本框(佔據剩餘空間)
        gbc.gridy = 3; gbc.weighty = 100; gbc.gridheight = 10;
        gbc.fill = GridBagConstraints.BOTH;
        gbl.setConstraints(messageText, gbc);
        mainPanel.add(messageText);

        // 事件綁定與佈局生效
        sendButton.addActionListener(this);
        setLayout(new BorderLayout());
        add("Center", mainPanel);

        // 記錄 Servlet 基址(同源策略下通常為 Applet 所在服務器)
        servletBaseURL = getCodeBase();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == sendButton) {
            sendMessage();
        }
    }

    /** 核心發送邏輯 */
    private void sendMessage() {
        message = sendText.getText();
        sendText.setText(""); // 清空輸入框
        showStatus("Message send!");
        messageText.setText("message"); // 臨時佔位

        try {
            // 構造查詢字符串:/servlet/Receive?message=URL編碼後內容
            String queryString = "/servlet/Receive?message=" + URLEncoder.encode(message);
            showStatus("Attempting to send: " + message);

            // 建立連接
            connection = new URL(servletBaseURL, queryString).openConnection();
            showStatus("Open Connection!");

            // 關鍵連接設置
            connection.setDefaultUseCaches(false);
            connection.setUseCaches(false);
            connection.setDoInput(true);   // 允許讀取響應
            connection.setDoOutput(false); // GET 請求無需輸出流
            connection.connect();

            showStatus("Open Stream!");
            DataInputStream in = new DataInputStream(connection.getInputStream());
            showStatus("Reading...");

            // 逐行讀取響應(此處僅保留最後一行)
            String line;
            while ((line = in.readLine()) != null) {
                messageText.setText(line);
            }
            in.close();

        } catch (MalformedURLException e2) {
            System.err.println("MalformedURLException: " + e2);
        } catch (IOException e1) {
            System.err.println("IOException: " + e1);
        }
    }

    // 生命週期佔位方法
    public synchronized void start() {}
    public synchronized void stop() {}
    public synchronized void destroy() {}
}

關鍵技術點解析

1. URLEncoder.encode() 的必要性

用戶輸入可能包含空格、中文、特殊符號,必須進行 URL 編碼,否則 Servlet 接收到的參數會截斷或亂碼。對應服務端需用 URLDecoder.decode() 解碼。

2. 連接參數的含義

方法設置值作用
setUseCachesfalse禁用瀏覽器/代理緩存,確保每次請求到達服務端
setDoInputtrue開啟輸入流讀取響應體
setDoOutputfalseGET 請求無請求體,設為 false;POST 需設 true 並獲取 OutputStream

3. 異步與線程安全

網絡 I/O 會阻塞 AWT 事件分發線程(EDT),導致界面凍結。生產環境應將 sendMessage() 放入獨立線程或使用 SwingWorker(Swing 環境),Applet 可用 new Thread(this::sendMessage).start()。

4. 同源策略限制

Applet 只能連接其代碼來源(getCodeBase())的主機/端口。跨域需服務端配置 crossdomain.xml(舊 Flash 模式)或服務端代理轉發。

現代化替代方案

  • Java Web Start / JNLP: 解決 Applet 瀏覽器插件依賴問題,但 Java 11 起已移除。
  • JavaFX + HTTP Client: 現代富客戶端首選,支持 HTTP/2、WebSocket、SSL/TLS 完整棧。
  • 前端 JS + REST API: 徹底瀏覽器原生,無插件,生態最豐富。

總結

本示例展示了 Java 早期網絡編程的經典模式:Applet 作為有狀態客戶端,通過標準 HTTP 協議與 Servlet 交互。雖然技術棧已過時,但其中的 URLConnection 配置、流處理、異常捕獲、編碼規範等核心知識點,在當前的微服務調用、HTTP Client 開發中依然完全適用。

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