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

從零手寫 Java Socket 聊天室(一):單客戶端 AWT 圖形界面版

     編輯:Java 相關知識

項目規劃四步走

  1. 單客戶端:AWT 組件 + Socket 實現客戶端持續發送消息給服務端(本文)。
  2. 多客戶端:引入多線程,服務端支持多用戶並發連接。
  3. 消息廣播:服務端轉發消息給所有在線客戶端,客戶端顯示聊天記錄。
  4. Web 化重構:前端換成 HTML/JS/WebSocket,後端升級 Netty/NIO。

核心難點

  • 首次使用 AWT 組件(Frame、TextArea、TextField)及事件監聽(ActionListener、WindowAdapter)。
  • 久未寫 CS 架構,Socket 通信細節生疏(輸出流 DataOutputStream、輸入流 DataInputStream、阻塞特性)。

客戶端完整代碼

package chat.chat;

import java.awt.*;
import java.awt.event.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class ChatClient extends Frame {
    private TextField tfTxt = new TextField();      // 輸入框
    private TextArea taContent = new TextArea();    // 顯示區
    private Socket socket;
    private DataOutputStream dos;

    public static void main(String[] args) {
        new ChatClient().launchFrame();
    }

    public void launchFrame() {
        setLocation(300, 200);
        setSize(400, 300);
        add(tfTxt, BorderLayout.SOUTH);
        add(taContent, BorderLayout.CENTER);
        pack();

        // 關閉窗口事件
        addWindowListener(new WindowAdapter() {
            @Override public void windowClosing(WindowEvent e) {
                disconnect();
                System.exit(0);
            }
        });

        // 回車發送消息
        tfTxt.addActionListener(e -> {
            String msg = tfTxt.getText().trim();
            if (!msg.isEmpty()) {
                taContent.append("我: " + msg + "\n");
                tfTxt.setText("");
                sendMessage(msg);
            }
        });

        setVisible(true);
        connect();
    }

    private void connect() {
        try {
            socket = new Socket("127.0.0.1", 8888);
            dos = new DataOutputStream(socket.getOutputStream());
            taContent.append("系統: 已連接服務器\n");
        } catch (Exception e) {
            taContent.append("系統: 連接失敗 - " + e.getMessage() + "\n");
            e.printStackTrace();
        }
    }

    private void sendMessage(String text) {
        try {
            dos.writeUTF(text);
            dos.flush();
        } catch (IOException e) {
            taContent.append("系統: 發送失敗\n");
            e.printStackTrace();
        }
    }

    private void disconnect() {
        try {
            if (dos != null) dos.close();
            if (socket != null) socket.close();
        } catch (IOException e) { e.printStackTrace(); }
    }
}

服務端完整代碼(單線程阻塞版)

package chat.chat;

import java.io.*;
import java.net.*;

public class ChatServer {
    public static void main(String[] args) {
        ServerSocket ss = null;
        Socket socket = null;
        DataInputStream dis = null;
        boolean running = true;

        try {
            ss = new ServerSocket(8888);
            System.out.println("服務器啟動,端口 8888...");
        } catch (BindException e) {
            System.err.println("端口被佔用"); System.exit(1);
        } catch (IOException e) { e.printStackTrace(); }

        try {
            while (running) {
                socket = ss.accept(); // 阻塞等待連接
                System.out.println("客戶端連接: " + socket.getInetAddress());
                dis = new DataInputStream(socket.getInputStream());

                while (running) {
                    String msg = dis.readUTF(); // 阻塞讀取
                    System.out.println("收到: " + msg);
                    // TODO: 此處應轉發給所有客戶端(下一版實現)
                }
            }
        } catch (EOFException e) {
            System.out.println("客戶端斷開");
        } catch (IOException e) { e.printStackTrace(); }
        finally {
            closeQuietly(dis, socket, ss);
        }
    }

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

運行測試

  1. 先啟動 ChatServer.main,控制台顯示「服務器啟動」。
  2. 再啟動 ChatClient.main,彈出窗口,底部輸入框敲字按回車。
  3. 服務端控制台打印收到的消息,客戶端窗口顯示「我: xxx」。

已知局限(下一版解決)

  • 服務端 accept() 後進入內層 while 循環讀取單客戶端消息,導致主循環無法再次 accept() → 不支持多客戶端並發。
  • 客戶端無接收消息線程,無法顯示服務端推送/其他用戶消息。
  • AWT 界面樸素,後續遷移至 Swing/JavaFX 或 Web。

下一步預告

引入線程池或每連接一線程模型,服務端維護 List<DataOutputStream> 實現廣播;客戶端啟動獨立讀取線程 new Thread(() -> { while(true) ta.append(dis.readUTF()); }).start() 實現雙向通信。

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