返回

JDK21 揭秘网络编程利器:用 TCP 构建服务器

后端

网络编程是后端开发领域的必修课,而 HTTP 服务器则是 Web 开发的核心。本小册将以 JDK8 到 JDK21 发布的新语法特性为基础,从头开始构建一个基于 TCP 协议的网络框架!

借助这些新特性,我们可以使用更简洁、更具表现力的代码,打造一个强大的网络框架。

小册主要内容:

  • 构建一个 TCP 服务器
  • 了解 HTTP 协议的运作方式
  • 实现常见 HTTP 方法(GET、POST 等)
  • 使用 HTTPS 保护数据安全
  • 部署服务器并进行测试

基础篇:TCP 服务器从零搭建

我们从最基础的 TCP 服务器搭建开始。首先,我们来了解一下 TCP 协议。

TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输层协议,它可以确保数据在网络上可靠地传输。TCP 在数据传输前会先建立连接,并在数据传输过程中对数据进行校验,如果发现错误就会重新发送数据。

为了构建一个 TCP 服务器,我们需要:

  1. 创建一个套接字(Socket)对象。
  2. 将套接字绑定到一个端口。
  3. 监听套接字上的连接请求。
  4. 接受连接请求,并创建一个新的套接字对象来处理该连接。

我们可以使用 Java 的 ServerSocketSocket 类来实现这些操作。下面是一个简单的 TCP 服务器示例:

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

public class TcpServer {

    public static void main(String[] args) throws IOException {
        // 创建一个ServerSocket对象,并将其绑定到端口8080
        ServerSocket serverSocket = new ServerSocket(8080);

        // 监听ServerSocket上的连接请求
        while (true) {
            // 接受连接请求,并创建一个新的Socket对象来处理该连接
            Socket socket = serverSocket.accept();

            // 处理连接请求,例如读取数据、发送数据等
            // ...

            // 关闭Socket对象
            socket.close();
        }

        // 关闭ServerSocket对象
        serverSocket.close();
    }
}

进阶篇:HTTP 服务器构建

在了解了 TCP 服务器的基本原理后,我们可以进一步构建一个 HTTP 服务器。

HTTP(Hypertext Transfer Protocol)是一种用于在万维网上进行数据传输的协议,它规定了客户端和服务器之间如何交换数据。HTTP 服务器需要能够处理 HTTP 请求,并根据请求返回相应的响应。

要构建一个 HTTP 服务器,我们需要:

  1. 创建一个 TCP 服务器。
  2. 解析 HTTP 请求。
  3. 根据 HTTP 请求生成响应。
  4. 将响应发送给客户端。

我们可以使用 Java 的 HttpServer 类来简化 HTTP 服务器的构建。HttpServer 类提供了许多便捷的方法,可以帮助我们轻松地处理 HTTP 请求和响应。

下面是一个简单的 HTTP 服务器示例:

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;

public class HttpServer {

    public static void main(String[] args) throws IOException {
        // 创建一个HttpServer对象,并将其绑定到端口8080
        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);

        // 设置请求处理程序
        server.createContext("/", new HttpHandler() {
            @Override
            public void handle(HttpExchange exchange) throws IOException {
                // 解析HTTP请求
                String requestMethod = exchange.getRequestMethod();
                String requestURI = exchange.getRequestURI().toString();
                Map<String, String> requestHeaders = getRequestHeaders(exchange);
                String requestBody = getRequestBody(exchange);

                // 生成HTTP响应
                String responseBody = "Hello World!";
                byte[] responseBytes = responseBody.getBytes(StandardCharsets.UTF_8);
                int responseCode = HttpURLConnection.HTTP_OK;
                Map<String, String> responseHeaders = new HashMap<>();
                responseHeaders.put("Content-Type", "text/plain");

                // 发送HTTP响应
                exchange.sendResponseHeaders(responseCode, responseBytes.length);
                exchange.getResponseBody().write(responseBytes);
                exchange.close();
            }
        });

        // 启动HTTP服务器
        server.start();
    }

    private static Map<String, String> getRequestHeaders(HttpExchange exchange) {
        Map<String, String> headers = new HashMap<>();
        for (Map.Entry<String, List<String>> entry : exchange.getRequestHeaders().entrySet()) {
            headers.put(entry.getKey(), entry.getValue().get(0));
        }
        return headers;
    }

    private static String getRequestBody(HttpExchange exchange) throws IOException {
        InputStreamReader isr = new InputStreamReader(exchange.getRequestBody(), StandardCharsets.UTF_8);
        BufferedReader br = new BufferedReader(isr);
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        br.close();
        isr.close();
        return sb.toString();
    }
}

这个 HTTP 服务器可以处理 GET 请求,并返回一个简单的“Hello World!”响应。我们可以通过在浏览器中输入 http://localhost:8080 来访问这个服务器。

高阶篇:HTTPS 安全防护

为了确保数据传输的安全性,我们可以使用 HTTPS(Hypertext Transfer Protocol Secure)协议。HTTPS 是 HTTP 的安全版本,它通过使用加密技术来保护数据在网络上的传输。

要构建一个 HTTPS 服务器,我们需要:

  1. 生成一个 SSL 证书。
  2. 将 SSL 证书导入到服务器。
  3. 配置服务器使用 SSL 证书。

我们可以使用 Java 的 KeyStoreSSLContext 类来实现这些操作。下面是一个简单的 HTTPS 服务器示例:

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;

public class HttpsServer {

    public static void main(String[] args) throws IOException, KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
        // 加载SSL证书
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(new FileInputStream("mykeystore.jks"), "password".toCharArray());

        // 创建KeyManagerFactory对象
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keyStore, "password".toCharArray());

        // 创建SSLContext对象
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(keyManagerFactory.getKeyManagers(), null, null);

        // 创建SSLServerSocketFactory对象
        SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();

        // 创建一个HttpServer对象,并将其绑定到端口8443
        HttpServer server = HttpServer.create(new InetSocketAddress(8443), 0);

        // 设置请求处理程序
        server.createContext("/", new HttpHandler() {
            @Override
            public void handle(HttpExchange exchange) throws IOException {
                // 解析HTTP请求
                String requestMethod =