多线程 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。