Java 多线程 HTTP 服务示例

多线程 HTTP 服务示例

这个示例演示如何用 ServerSocket 接收 HTTP 请求,并为每个连接创建一个线程处理请求。

服务端入口

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);

        while (true) {
            Socket socket = serverSocket.accept();
            Thread thread = new Thread(new MyRunnable(socket));
            thread.start();
        }
    }
}

请求处理线程

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class MyRunnable implements Runnable {
    private final Socket socket;

    public MyRunnable(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            handle();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void handle() throws IOException {
        try (
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8))
        ) {
            String firstLine = reader.readLine();
            boolean isHomeRequest = firstLine != null && firstLine.startsWith("GET / HTTP/1.");

            String header;
            while ((header = reader.readLine()) != null && !header.isEmpty()) {
                System.out.println(header);
            }

            if (!isHomeRequest) {
                writer.write("HTTP/1.0 404 Not Found\r\n");
                writer.write("Content-Length: 0\r\n");
                writer.write("\r\n");
                writer.flush();
                return;
            }

            String html = readHtml("HttpDemo/src/html.html");
            int length = html.getBytes(StandardCharsets.UTF_8).length;

            writer.write("HTTP/1.0 200 OK\r\n");
            writer.write("Content-Length: " + length + "\r\n");
            writer.write("Content-Type: text/html; charset=utf-8\r\n");
            writer.write("\r\n");
            writer.write(html);
            writer.flush();
        }
    }

    private String readHtml(String path) throws IOException {
        StringBuilder builder = new StringBuilder();

        try (
            FileInputStream inputStream = new FileInputStream(path);
            BufferedReader htmlReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))
        ) {
            String line;
            while ((line = htmlReader.readLine()) != null) {
                builder.append(line);
            }
        }

        return builder.toString();
    }
}

注意点

  • accept() 会阻塞,直到有新的客户端连接。
  • 每个请求都创建新线程,适合理解原理;真实项目应使用线程池。
  • Content-Length 需要计算 UTF-8 编码后的字节数,而不是字符串长度。
  • 响应头结束后必须写入一个空行,也就是 \r\n