1. 概述
HTTP服务器通常为请求客户端提供资源。Java中已有多种生产级Web服务器,但通过实现一个基于ServerSocket
的服务器,能帮助我们理解HTTP服务器的工作原理。
ServerSocket
类允许我们创建一个监听特定IP地址和端口TCP连接的服务器。本文将学习如何使用ServerSocket
构建简单服务器,并实现GET请求处理。⚠️ 注意:此服务器仅用于教学目的,不适合生产环境。
2. ServerSocket基础原理
服务器首先监听来自客户端的连接(可能是浏览器、API工具等)。连接建立后,服务器向客户端提供资源作为响应。
ServerSocket
类提供在指定端口创建服务器的方法,通过accept()
方法监听传入连接。该方法是阻塞式的,直到建立连接才会返回Socket
实例。Socket
实例提供了服务器与客户端通信的输入输出流通道。
3. 创建ServerSocket实例
首先创建指定端口的ServerSocket
对象:
int port = 8080;
ServerSocket serverSocket = new ServerSocket(port);
然后使用accept()
方法接受传入连接:
while (true) {
Socket clientSocket = serverSocket.accept();
// ...
}
这段代码通过while
循环持续等待连接。当连接建立时,accept()
方法返回Socket
对象,使服务器和客户端能通过已建立的网络通信。
4. 处理输入输出
服务器需要接收客户端输入并返回响应。使用Socket
类的getInputStream()
和getOutputStream()
方法获取通信流:
while (true) {
// ...
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream())
);
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(clientSocket.getOutputStream())
);
// ...
}
✅ 代码解析:
getInputStream()
获取客户端-服务器连接的输入流,用BufferedReader
包装高效读取文本数据getOutputStream()
用BufferedWriter
包装,方便服务器发送响应
输入通常是HTTP请求(如GET http://localhost:8080
)。接下来构建响应:
String body = """
<html>
<head>
<title>Baeldung Home</title>
</head>
<body>
<h1>Baeldung Home Page</h1>
<p>Java Tutorials</p>
<ul>
<li>
<a href="/get-started-with-java-series"> Java </a>
</li>
<li>
<a href="/spring-boot"> Spring </a>
</li>
<li>
<a href="/learn-jpa-hibernate"> Hibernate </a>
</li>
</ul>
</body>
</html>
""";
计算响应体长度用于HTTP头:
int length = body.length();
最后写入HTTP头和响应体:
while (true) {
// ...
String clientInputLine;
while ((clientInputLine = in.readLine()) != null) {
if (clientInputLine.isEmpty()) {
break;
}
out.write("HTTP/1.0 200 OK\r\n");
out.write("Date: " + now + "\r\n");
out.write("Server: Custom Server\r\n");
out.write("Content-Type: text/html\r\n");
out.write("Content-Length: " + length + "\r\n");
out.write("\r\n");
out.write(body);
}
}
⚠️ 关键点:用\r\n
(空行)分隔HTTP头和响应体,这是HTTP协议的硬性要求。
5. 多线程服务器
单线程服务器性能堪忧,生产级服务器必须支持并发请求处理。下面重构为多线程版本:
class SimpleHttpServerMultiThreaded {
private final int port;
private static final int THREAD_POOL_SIZE = 10;
public SimpleHttpServerMultiThreaded(int port) {
this.port = port;
}
}
定义客户端处理方法:
void handleClient(Socket clientSocket) {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(clientSocket.getOutputStream()))
) {
String clientInputLine;
while ((clientInputLine = in.readLine()) != null) {
if (clientInputLine.isEmpty()) {
break;
}
}
LocalDateTime now = LocalDateTime.now();
out.write("HTTP/1.0 200 OK\r\n");
out.write("Date: " + now + "\r\n");
out.write("Server: Custom Server\r\n");
out.write("Content-Type: text/html\r\n");
out.write("Content-Length: " + length + "\r\n");
out.write("\r\n");
out.write(body);
} catch (IOException e) {
// ...
} finally {
try {
clientSocket.close();
} catch (IOException e) {
// ...
}
}
}
创建启动方法处理并发连接:
void start() throws IOException {
try (ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
ServerSocket serverSocket = new ServerSocket(port)) {
while (true) {
Socket clientSocket = serverSocket.accept();
threadPool.execute(() -> handleClient(clientSocket));
}
}
}
✅ 核心改进:
- 使用
ExecutorService
创建固定大小线程池 - 通过
execute()
提交连接处理任务 - 每次客户端连接都会创建新的
Socket
实例,确保通信通道独立
6. 服务器测试
在main
方法中启动服务器:
static void main(String[] args) throws IOException {
int port = 8080;
SimpleHttpServerMultiThreaded server = new SimpleHttpServerMultiThreaded(port);
server.start();
}
浏览器访问http://localhost:8080
测试:
7. 总结
本文通过ServerSocket
实现了简单HTTP服务器,包括:
- 单线程服务器基础实现
- 多线程服务器性能优化
- HTTP请求/响应处理流程
虽然代码简单粗暴,但完整展示了HTTP服务器的核心原理。生产环境中建议使用成熟的Web服务器框架(如Tomcat、Netty),避免重复造轮子。