凌峰创科服务平台

Java socket如何实现HTTP服务器?

我们将分步进行,从最基础的服务器启动,到解析请求、处理请求,最后返回响应。

Java socket如何实现HTTP服务器?-图1
(图片来源网络,侵删)

核心概念回顾

在开始编码前,我们先回顾一下几个关键概念:

  • Socket (套接字):是网络编程的 API,它提供了两台计算机之间进行通信的端点,在 Java 中,java.net.ServerSocket 用于服务器端,等待客户端连接;java.net.Socket 用于客户端,或服务器端与已连接的客户端通信。
  • HTTP (超文本传输协议):是 Web 的基础协议,它是一种基于文本的、无状态的请求/响应协议,一个典型的 HTTP 交互包括:
    1. 客户端请求:客户端(如浏览器)向服务器发送一个请求报文,包含请求方法(如 GET, POST)、路径、HTTP 版本和一系列请求头。
    2. 服务器响应:服务器解析请求,处理后,向客户端返回一个响应报文,包含状态码(如 200 OK, 404 Not Found)、响应头和响应体(通常是 HTML 内容)。

步骤一:创建一个基础服务器(仅接受连接)

我们先创建一个最简单的服务器,它只负责监听端口并接受客户端连接,但不做任何处理。

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class SimpleHttpServer {
    // 定义服务器监听的端口号
    private static final int PORT = 8080;
    public static void main(String[] args) {
        // 使用 try-with-resources 确保 ServerSocket 被正确关闭
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("服务器启动,监听端口: " + PORT);
            // 进入无限循环,持续等待客户端连接
            while (true) {
                // accept() 方法会阻塞,直到一个客户端连接进来
                Socket clientSocket = serverSocket.accept();
                System.out.println("新的客户端连接: " + clientSocket.getInetAddress().getHostAddress());
                // 为了不阻塞主线程处理下一个连接,我们为每个客户端连接创建一个新线程
                new Thread(new ClientHandler(clientSocket)).start();
            }
        } catch (IOException e) {
            System.err.println("服务器启动失败或发生 I/O 错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
// 客户端连接处理器
class ClientHandler implements Runnable {
    private final Socket clientSocket;
    public ClientHandler(Socket socket) {
        this.clientSocket = socket;
    }
    @Override
    public void run() {
        // 在这里我们将处理客户端的请求和响应
        System.out.println(Thread.currentThread().getName() + " 正在处理客户端: " + clientSocket.getInetAddress());
        // 为了演示,我们暂时只打印信息并关闭连接
        try {
            // 获取输入流,可以读取客户端发送的数据
            // clientSocket.getInputStream();
            // 获取输出流,可以向客户端发送数据
            // clientSocket.getOutputStream();
            System.out.println("处理完毕,关闭连接。");
            clientSocket.close();
        } catch (IOException e) {
            System.err.println("处理客户端时发生错误: " + e.getMessage());
        }
    }
}

如何运行:

  1. 将代码保存为 SimpleHttpServer.java
  2. 编译:javac SimpleHttpServer.java
  3. 运行:java SimpleHttpServer
  4. 你会看到控制台输出 "服务器启动,监听端口: 8080"。
  5. 打开浏览器,访问 http://localhost:8080,然后观察服务器控制台的输出。

步骤二:解析 HTTP 请求

我们来完善 ClientHandler,使其能够读取客户端(浏览器)发送的原始 HTTP 请求报文。

Java socket如何实现HTTP服务器?-图2
(图片来源网络,侵删)

HTTP 请求报文由三部分组成:请求行、请求头、请求体,对于 GET 请求,通常没有请求体,我们可以按行读取,直到遇到一个空行(\r\n),这标志着请求头的结束。

// 在 ClientHandler 的 run() 方法中更新以下代码
@Override
public void run() {
    try (
        // 使用 try-with-resources 自动关闭流
        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true) // autoFlush=true
    ) {
        System.out.println(Thread.currentThread().getName() + " 正在处理客户端: " + clientSocket.getInetAddress());
        // 1. 读取并解析 HTTP 请求
        String requestLine = in.readLine();
        if (requestLine == null) {
            return; // 客户端可能断开连接
        }
        System.out.println("收到请求行: " + requestLine);
        // 解析请求行,格式为 "METHOD PATH PROTOCOL"
        String[] requestParts = requestLine.split(" ");
        if (requestParts.length < 3) {
            sendErrorResponse(out, 400, "Bad Request");
            return;
        }
        String method = requestParts[0];
        String path = requestParts[1];
        String httpVersion = requestParts[2];
        System.out.println("请求方法: " + method);
        System.out.println("请求路径: " + path);
        System.out.println("HTTP版本: " + httpVersion);
        // 读取请求头,直到遇到空行
        String headerLine;
        while ((headerLine = in.readLine()) != null && !headerLine.isEmpty()) {
            System.out.println("请求头: " + headerLine);
        }
        // 2. 处理请求并生成响应
        // ... (下一步实现)
        // 3. 发送响应
        // ... (下一步实现)
        System.out.println("处理完毕,关闭连接。");
    } catch (IOException e) {
        System.err.println("处理客户端时发生错误: " + e.getMessage());
    }
}
// 辅助方法:发送错误响应
private void sendErrorResponse(PrintWriter out, int statusCode, String statusMessage) {
    out.println("HTTP/1.1 " + statusCode + " " + statusMessage);
    out.println("Content-Type: text/html");
    out.println("Connection: close");
    out.println(); // 空行,分隔响应头和响应体
    out.println("<html><body><h1>" + statusCode + " " + statusMessage + "</h1></body></html>");
}

运行并测试: 再次运行服务器,然后用浏览器访问 http://localhost:8080,你会看到服务器控制台打印出了详细的请求信息,包括 GET / HTTP/1.1 和各种请求头(如 Host, User-Agent 等)。

步骤三:处理请求并发送 HTTP 响应

我们根据请求的路径,生成相应的 HTTP 响应,我们将实现一个非常简单的路由:访问 返回 "Hello, World!",访问 /about 返回 "About Page",其他路径返回 404 Not Found。

// 在 ClientHandler 的 run() 方法中,在解析完请求后添加以下代码
// 2. 处理请求并生成响应
String responseContent;
String contentType;
int statusCode;
String statusMessage;
if ("/".equals(path)) {
    responseContent = "<html><head><title>Home</title></head><body><h1>Hello, World!</h1><p>欢迎来到我的 Java HTTP 服务器!</p></body></html>";
    contentType = "text/html";
    statusCode = 200;
    statusMessage = "OK";
} else if ("/about".equals(path)) {
    responseContent = "<html><head><title>About</title></head><body><h1>About Page</h1><p>这是一个用 Java Socket 实现的简单 HTTP 服务器。</p></body></html>";
    contentType = "text/html";
    statusCode = 200;
    statusMessage = "OK";
} else {
    responseContent = "<html><head><title>404 Not Found</title></head><body><h1>404 Not Found</h1><p>你请求的页面不存在。</p></body></html>";
    contentType = "text/html";
    statusCode = 404;
    statusMessage = "Not Found";
}
// 3. 发送响应
out.println("HTTP/1.1 " + statusCode + " " + statusMessage);
out.println("Content-Type: " + contentType);
out.println("Content-Length: " + responseContent.getBytes(StandardCharsets.UTF_8).length);
out.println("Connection: close"); // 告诉客户端连接将在响应后关闭
out.println(); // 空行,非常重要,分隔响应头和响应体
out.println(responseContent); // 发送响应体

别忘了添加 import: import java.nio.charset.StandardCharsets;

完整代码

将所有部分整合起来,这就是一个功能完整的、简单的 Java HTTP 服务器。

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset
分享:
扫描分享到社交APP
上一篇
下一篇