S SmartDocs
シリーズ: Angelia java 68 行 · 更新日 2026-09-27

EchoServer.java

Angelia/Java/Week_6_7/code/08_chat_protocol/EchoServer.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 最小的「一行一則訊息」伺服器:示範 Java 網路程式的基本骨架。
 *
 * 每收到一行,就用 ChatMessage.parse 解析;合法就回 OK(或 MSG 回送),不合法就回 ERR。
 * 收到 QUIT 就回 BYE 並關閉該連線。
 *
 * 執行:java EchoServer 5555
 * 另開一個終端機:java EchoClient localhost 5555   (或 nc localhost 5555 手動輸入)
 *
 * 注意:這個版本一次只服務一位客戶(單執行緒)。
 * 要同時服務多位客戶需要「多執行緒」,那是 Week 8 Workshop 的主題。
 */
public class EchoServer {

    public static void main(String[] args) throws IOException {
        int port = args.length > 0 ? Integer.parseInt(args[0]) : 5555;
        try (ServerSocket server = new ServerSocket(port)) {          // 在 port 上「聽」
            System.out.println("伺服器啟動,監聽 port " + port);
            while (true) {
                try (Socket client = server.accept();                 // 阻塞直到有人連進來
                     BufferedReader in = new BufferedReader(
                             new InputStreamReader(client.getInputStream(), "UTF-8"));
                     PrintWriter out = new PrintWriter(client.getOutputStream(), true, java.nio.charset.StandardCharsets.UTF_8)) {
                    System.out.println("客戶端已連線:" + client.getRemoteSocketAddress());
                    handle(in, out);
                    System.out.println("客戶端已離線");
                }
            }
        }
    }

    /** 對「一個」客戶端的完整對話:一直讀到 QUIT 或連線中斷。 */
    static void handle(BufferedReader in, PrintWriter out) throws IOException {
        String name = null;
        String line;
        while ((line = in.readLine()) != null) {                      // readLine 回傳 null = 對方關閉了連線
            ChatMessage reply;
            try {
                ChatMessage m = ChatMessage.parse(line);
                switch (m) {
                    case ChatMessage.Login l -> {
                        name = l.name();
                        reply = new ChatMessage.Ok("welcome " + name);
                    }
                    case ChatMessage.Join j -> reply = new ChatMessage.Ok("joined " + j.room());
                    case ChatMessage.Say s -> reply = name == null
                            ? new ChatMessage.Err(401, "login first")
                            : new ChatMessage.Msg(s.room(), name, s.text());
                    case ChatMessage.Quit q -> {
                        out.println(ChatMessage.encode(new ChatMessage.Bye()));
                        return;
                    }
                    default -> reply = new ChatMessage.Err(400, "clients may not send " + line.split(" ")[0]);
                }
            } catch (ChatMessage.ProtocolException e) {
                reply = new ChatMessage.Err(400, e.getMessage());
            }
            out.println(ChatMessage.encode(reply));                   // println 會加上換行並 flush
        }
    }
}

関連記事