我们也会介绍现代、更推荐的 Java 11+ 内置 HTTP 客户端 java.net.http.HttpClient,以及业界非常流行的 Netty 框架。

HTTP 服务端
我们将创建一个简单的服务端,它监听指定端口,接收客户端的请求,并返回一个 "Hello, World!" 响应。
核心类
java.net.ServerSocket: 用于在服务器上监听客户端的连接请求。java.net.Socket: 代表一个客户端与服务端之间的连接。java.io.InputStream: 从连接中读取客户端发送的数据(请求)。java.io.OutputStream: 向连接中写入数据(响应)。
代码示例
// SimpleHttpServer.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class SimpleHttpServer {
public static void main(String[] args) {
// 定义服务端监听的端口号
int port = 8080;
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("Server is listening on port " + port);
// 无限循环,持续等待客户端连接
while (true) {
// accept() 方法会阻塞,直到有客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("New client connected: " + clientSocket.getInetAddress().getHostAddress());
// 为每个客户端连接创建一个新线程来处理,这样服务端可以同时处理多个请求
new Thread(new ClientHandler(clientSocket)).start();
}
} catch (IOException e) {
System.err.println("Server exception: " + e.getMessage());
e.printStackTrace();
}
}
// 内部类,用于处理单个客户端的请求
static class ClientHandler implements Runnable {
private final Socket clientSocket;
public ClientHandler(Socket socket) {
this.clientSocket = socket;
}
@Override
public void run() {
try (
// 获取输入流,用于读取客户端的请求
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// 获取输出流,用于向客户端发送响应
OutputStream out = clientSocket.getOutputStream()
) {
// 读取客户端请求的第一行(请求行)
String requestLine = in.readLine();
System.out.println("Received request: " + requestLine);
// 读取请求头,直到遇到一个空行
String headerLine;
while ((headerLine = in.readLine()) != null && !headerLine.isEmpty()) {
// 可以在这里处理请求头,Content-Length
System.out.println("Header: " + headerLine);
}
// HTTP 响应
String httpResponse = "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/plain\r\n" +
"Connection: close\r\n" +
"\r\n" + // 空行,分隔响应头和响应体
"Hello, World!";
// 将响应写入输出流
out.write(httpResponse.getBytes());
out.flush();
System.out.println("Response sent to " + clientSocket.getInetAddress().getHostAddress());
} catch (IOException e) {
System.err.println("Error handling client: " + e.getMessage());
} finally {
try {
// 关闭客户端连接
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
如何运行
- 将代码保存为
SimpleHttpServer.java。 - 编译:
javac SimpleHttpServer.java - 运行:
java SimpleHttpServer - 你会看到控制台输出:
Server is listening on port 8080。 - 服务端已经启动并等待连接。
HTTP 客户端
我们将创建一个客户端,它会连接到上面启动的服务端(地址为 localhost:8080),发送一个 HTTP GET 请求,并打印服务端返回的响应。
我们将介绍两种方式:
- 传统方式:
java.net.HttpURLConnection - 现代方式 (Java 11+):
java.net.http.HttpClient
传统 HttpURLConnection
这是在 Java 11 之前最常用的标准 HTTP 客户端 API。

核心类
java.net.URL: 表示要请求的资源地址。java.net.HttpURLConnection: 代表一个 HTTP 连接。
代码示例
// SimpleHttpClient.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class SimpleHttpClient {
public static void main(String[] args) {
// 目标 URL
String urlString = "http://localhost:8080/";
try {
URL url = new URL(urlString);
// 打开一个 HttpURLConnection 连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法为 GET
connection.setRequestMethod("GET");
// 获取响应码
int responseCode = connection.getResponseCode();
System.out.println("Sending 'GET' request to URL : " + urlString);
System.out.println("Response Code : " + responseCode);
// 如果响应码是 200 (OK),则读取响应体
if (responseCode == HttpURLConnection.HTTP_OK) {
// 使用 try-with-resources 自动关闭流
try (BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()))) {
String inputLine;
StringBuilder response = new StringBuilder();
// 逐行读取响应
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
// 打印完整的响应
System.out.println("Response : " + response.toString());
}
} else {
System.out.println("GET request not worked");
}
} catch (IOException e) {
System.err.println("Client exception: " + e.getMessage());
e.printStackTrace();
}
}
}
现代 HttpClient (推荐,Java 11+)
Java 11 引入了全新的、更现代、更易用的 HttpClient API,它支持异步请求、WebSocket,API 设计更符合现代编程风格。
核心类
java.net.http.HttpClient: 客户端实例,可以配置各种参数。java.net.http.HttpRequest: 代表一个 HTTP 请求。java.net.http.HttpResponse: 代表一个 HTTP 响应。
代码示例
// ModernHttpClient.java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class ModernHttpClient {
public static void main(String[] args) {
// 目标 URL
String urlString = "http://localhost:8080/";
// 1. 创建 HttpClient 实例
// 可以配置连接超时、代理等
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1) // 使用 HTTP/1.1
.connectTimeout(Duration.ofSeconds(5)) // 连接超时 5 秒
.build();
// 2. 创建 HttpRequest 实例
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(urlString))
.header("User-Agent", "Java HttpClient") // 设置请求头
.GET() // 显式指定 GET 请求
.build();
try {
// 3. 发送请求并同步获取响应
// 发送是异步的,但 get() 会阻塞直到响应返回
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 4. 处理响应
System.out.println("Status code: " + response.statusCode());
System.out.println("Response headers: " + response.headers());
System.out.println("Response body: " + response.body());
} catch (Exception e) {
System.err.println("Client exception: " + e.getMessage());
e.printStackTrace();
}
}
}
异步请求示例:
HttpClient 的强大之处在于异步处理。
// ... (HttpClient 和 HttpRequest 的创建代码同上)
// 异步发送请求
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body) // 当响应到达时,提取 body
.thenAccept(System.out::println) // 然后打印 body
.join(); // 等待异步操作完成,否则程序会立即退出
使用 Netty 框架 (进阶)
对于高性能、高并发的网络应用,直接使用 ServerSocket 和 Socket 会比较复杂,Netty 是一个成熟的、高性能的 NIO 客户端/服务器框架,极大地简化了网络编程。
为什么选择 Netty?
- 高性能: 基于 NIO (非阻塞 I/O),能轻松处理高并发连接。
- 功能丰富: 内置了编解码器(如 HTTP、Protobuf、JSON)、SSL/TLS 支持等。
- API 友好: 提供了清晰、易于使用的 API。
服务端示例 (使用 Netty)
你需要添加 Netty 依赖 (Maven):

<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.86.Final</version> <!-- 请使用最新版本 -->
</dependency>
// NettyHttpServer.java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
public class NettyHttpServer {
private final int port;
public NettyHttpServer(int port) {
this.port = port;
}
public void run() throws Exception {
// EventLoopGroup 是 Netty 的处理核心,类似线程池
// bossGroup 接受连接
// workerGroup 处理业务
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // 指定使用 NIO 传输
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
// 添加 HTTP 编解码器,Netty 会自动处理 HTTP 协议的细节
ch.pipeline().addLast(new HttpServerCodec());
// 添加自定义的业务处理器
ch.pipeline().addLast(new SimpleHttpServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // 设置队列连接数
.childOption(ChannelOption.SO_KEEPALIVE, true); // 保持连接
System.out.println("Netty Server started on port " + port);
// 绑定端口,开始接收连接
ChannelFuture f = b.bind(port).sync();
// 等待服务器 socket 关闭
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new NettyHttpServer(8080).run();
}
}
自定义业务处理器 SimpleHttpServerHandler.java:
// SimpleHttpServerHandler.java
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.util.CharsetUtil;
public class SimpleHttpServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
try {
// 解析请求路径等
System.out.println("Received request: " + request.uri());
// 构建响应
String responseContent = "Hello from Netty!";
// Netty 提供了方便的工具类来构建 FullHttpResponse
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.copiedBuffer(responseContent, CharsetUtil.UTF_8)
);
// 设置响应头
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
// 如果请求是 Keep-Alive,则保持连接
if (HttpUtil.isKeepAlive(request)) {
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
}
// 发送响应
ctx.writeAndFlush(response);
} finally {
// 释放请求资源
ReferenceCountUtil.release(request);
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
总结与对比
| 特性 | java.net.* (原生) |
java.net.http.HttpClient (Java 11+) |
Netty 框架 |
|---|---|---|---|
| 易用性 | 服务端复杂,客户端较繁琐 | 非常简单,API 现代化 | 较复杂,需要理解 Netty 的概念(Pipeline, Handler) |
| 性能 | 一般,基于 BIO (阻塞 I/O) | 较好,基于 NIO | 非常高,专为高并发设计 |
| 功能 | 基础的 HTTP 功能 | 支持 HTTP/1.1, HTTP/2, WebSocket, 异步 | 极其丰富,支持多种协议,可扩展性强 |
| 依赖 | 无,JDK 自带 | 无,JDK 自带 (11+) | 需要引入外部库 |
| 适用场景 | - 简单的脚本工具 - 学习网络协议基础 - 不想引入第三方依赖的项目 |
- 现代 Java 应用的首选 - 需要异步请求或 WebSocket - 从旧版 API 迁移 |
- 高性能 Web 服务 - RPC 框架 - 网络游戏、聊天应用 - 需要处理复杂协议 |
建议:
- 对于初学者或简单应用: 从
java.net.HttpURLConnection和java.net.ServerSocket开始,能帮助你理解 HTTP 的基本原理。 - 对于新项目 (Java 11+): 强烈推荐使用
java.net.http.HttpClient,它兼顾了易用性和性能,是 Java 官方力推的现代化 HTTP 客户端。 - 对于高性能、高并发的生产环境: Netty 是不二之选,它是许多知名开源框架(如 Spring WebFlux, Dubbo, Elasticsearch)的底层网络库。
