本文目录导读:

我将为您提供几个Java代理服务器的完整案例,从简单到复杂,涵盖不同实现方式。
简单HTTP转发代理
这是一个基础的HTTP代理服务器,能够转发HTTP请求和响应:
import java.io.*;
import java.net.*;
public class SimpleHttpProxy {
private static final int PORT = 8080;
private static final int BUFFER_SIZE = 8192;
public static void main(String[] args) {
System.out.println("简单HTTP代理服务器启动,监听端口: " + PORT);
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("客户端连接: " + clientSocket.getInetAddress());
new Thread(() -> handleClient(clientSocket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleClient(Socket clientSocket) {
try (clientSocket;
InputStream clientIn = clientSocket.getInputStream();
OutputStream clientOut = clientSocket.getOutputStream()) {
// 读取客户端请求
byte[] requestBytes = readRequest(clientIn);
String request = new String(requestBytes);
if (request.isEmpty()) {
return;
}
System.out.println("收到请求: " + request.split("\n")[0]);
// 解析请求行,获取URL
String[] lines = request.split("\r\n");
String requestLine = lines[0];
String[] parts = requestLine.split(" ");
if (parts.length < 3) {
sendErrorResponse(clientOut, 400, "Bad Request");
return;
}
String url = parts[1];
System.out.println("请求URL: " + url);
// 转发请求到目标服务器
forwardRequest(clientOut, requestBytes, url);
} catch (Exception e) {
e.printStackTrace();
}
}
private static byte[] readRequest(InputStream in) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] temp = new byte[BUFFER_SIZE];
int read;
// 简单方式:读取直到连接关闭或者超时
try {
in.setSoTimeout(1000);
while ((read = in.read(temp)) != -1) {
buffer.write(temp, 0, read);
if (buffer.size() > BUFFER_SIZE * 100) break; // 防止过大请求
}
} catch (SocketTimeoutException e) {
// 超时说明请求读取完毕
}
return buffer.toByteArray();
}
private static void forwardRequest(OutputStream clientOut, byte[] requestBytes, String url)
throws IOException {
// 解析URL
URL targetUrl = new URL(url);
int port = targetUrl.getPort() == -1 ? 80 : targetUrl.getPort();
String host = targetUrl.getHost();
// 建立到目标服务器的连接
try (Socket targetSocket = new Socket(host, port);
OutputStream targetOut = targetSocket.getOutputStream();
InputStream targetIn = targetSocket.getInputStream()) {
// 发送请求到目标服务器
targetOut.write(requestBytes);
targetOut.flush();
// 接收响应并转发给客户端
byte[] buffer = new byte[BUFFER_SIZE];
int read;
while ((read = targetIn.read(buffer)) != -1) {
clientOut.write(buffer, 0, read);
clientOut.flush();
}
}
}
private static void sendErrorResponse(OutputStream out, int code, String message)
throws IOException {
String response = "HTTP/1.1 " + code + " " + message + "\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: " + message.length() + "\r\n" +
"Connection: close\r\n\r\n" +
message;
out.write(response.getBytes());
out.flush();
}
}
HTTPS CONNECT代理
支持HTTPS的CONNECT方法代理:
import java.io.*;
import java.net.*;
public class ConnectProxy {
private static final int PORT = 8080;
private static final int BUFFER_SIZE = 8192;
public static void main(String[] args) {
System.out.println("HTTPS CONNECT代理服务器启动,监听端口: " + PORT);
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
while (true) {
Socket clientSocket = serverSocket.accept();
new Thread(() -> handleClient(clientSocket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleClient(Socket clientSocket) {
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
// 读取请求头
String requestLine = reader.readLine();
System.out.println("请求行: " + requestLine);
if (requestLine == null) return;
String[] parts = requestLine.split(" ");
String method = parts[0];
if (!method.equals("CONNECT")) {
// 非CONNECT请求,返回405
sendResponse(clientSocket.getOutputStream(), 405,
"HTTP/1.1 405 Method Not Allowed\r\n\r\n");
return;
}
// 解析host:port
String[] hostPort = parts[1].split(":");
String host = hostPort[0];
int port = Integer.parseInt(hostPort[1]);
// 读取请求头
String line;
while (!(line = reader.readLine()).isEmpty()) {
System.out.println("请求头: " + line);
}
// 尝试连接目标服务器
try {
Socket targetSocket = new Socket(host, port);
System.out.println("连接目标服务器成功: " + host + ":" + port);
// 发送200响应
sendResponse(clientSocket.getOutputStream(), 200,
"HTTP/1.1 200 Connection Established\r\n\r\n");
// 建立双向隧道
Thread t1 = new Thread(() -> tunnel(clientSocket, targetSocket));
Thread t2 = new Thread(() -> tunnel(targetSocket, clientSocket));
t1.start();
t2.start();
// 等两个线程结束
t1.join();
t2.join();
} catch (IOException e) {
System.out.println("无法连接到目标服务器: " + host + ":" + port);
sendResponse(clientSocket.getOutputStream(), 502,
"HTTP/1.1 502 Bad Gateway\r\n\r\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void tunnel(Socket from, Socket to) {
try (InputStream in = from.getInputStream();
OutputStream out = to.getOutputStream()) {
byte[] buffer = new byte[BUFFER_SIZE];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
out.flush();
System.out.println("转发 " + read + " 字节");
}
} catch (IOException e) {
// 连接断开
}
}
private static void sendResponse(OutputStream out, int code, String response)
throws IOException {
out.write(response.getBytes());
out.flush();
}
}
轻量级高性能代理(基于NIO)
使用Java NIO实现的非阻塞代理服务器:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
public class NioProxyServer {
private static final int PORT = 8080;
private static final int BUFFER_SIZE = 65536;
// 用于记录管道连接
private static class Tunnel {
SocketChannel clientChannel;
SocketChannel targetChannel;
boolean clientClosed = false;
boolean targetClosed = false;
// 关闭连接
public synchronized void closeAll() {
try {
if (targetChannel != null) {
targetChannel.close();
}
if (clientChannel != null) {
clientChannel.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(PORT));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO代理服务器启动,监听端口: " + PORT);
while (true) {
// 等待事件
selector.select(1000);
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
// 接受新的客户端连接
acceptClient(selector, serverChannel);
} else if (key.isConnectable()) {
// 连接目标服务器完成
handleConnect(key);
} else if (key.isReadable()) {
// 有数据可读
handleRead(key, selector);
}
}
}
}
private static void acceptClient(Selector selector, ServerSocketChannel serverChannel)
throws IOException {
SocketChannel clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);
System.out.println("新客户端连接: " + clientChannel.getRemoteAddress());
// 注册读取事件处理CONNECT请求
clientChannel.register(selector, SelectionKey.OP_READ);
}
private static void handleConnect(SelectionKey key) {
Tunnel tunnel = (Tunnel) key.attachment();
SocketChannel targetChannel = (SocketChannel) key.channel();
try {
if (targetChannel.finishConnect()) {
System.out.println("目标服务器连接成功");
// 发送200响应给客户端
String response = "HTTP/1.1 200 Connection Established\r\n\r\n";
ByteBuffer buffer = ByteBuffer.wrap(response.getBytes());
tunnel.clientChannel.write(buffer);
// 注册双方读取事件
tunnel.clientChannel.register(key.selector(), SelectionKey.OP_READ, tunnel);
targetChannel.register(key.selector(), SelectionKey.OP_READ, tunnel);
System.out.println("隧道建立成功");
}
} catch (IOException e) {
e.printStackTrace();
tunnel.closeAll();
key.cancel();
}
}
private static void handleRead(SelectionKey key, Selector selector) {
Tunnel tunnel = (Tunnel) key.attachment();
SocketChannel readChannel = (SocketChannel) key.channel();
try {
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
int bytesRead = readChannel.read(buffer);
if (bytesRead == -1) {
// 连接已关闭
System.out.println("连接关闭");
tunnel.closeAll();
key.cancel();
return;
}
if (bytesRead > 0) {
buffer.flip();
// 确定是哪个方向的通道
SocketChannel writeChannel = null;
if (readChannel == tunnel.clientChannel) {
writeChannel = tunnel.targetChannel;
} else {
writeChannel = tunnel.clientChannel;
}
// 转发数据
writeChannel.write(buffer);
System.out.println("转发 " + bytesRead + " 字节");
}
} catch (IOException e) {
e.printStackTrace();
tunnel.closeAll();
key.cancel();
}
}
}
带有缓存功能的代理服务器
import java.io.*;
import java.net.*;
import java.util.concurrent.ConcurrentHashMap;
import java.nio.file.*;
public class HttpCacheProxy {
private static final int PORT = 8080;
private static final ConcurrentHashMap<String, CacheEntry> cache = new ConcurrentHashMap<>();
private static final long CACHE_DURATION = 5 * 60 * 1000; // 5分钟
static class CacheEntry {
byte[] data;
long timestamp;
CacheEntry(byte[] data, long timestamp) {
this.data = data;
this.timestamp = timestamp;
}
boolean isExpired() {
return System.currentTimeMillis() - timestamp > CACHE_DURATION;
}
}
public static void main(String[] args) {
System.out.println("缓存代理服务器启动,监听端口: " + PORT);
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
while (true) {
Socket clientSocket = serverSocket.accept();
new Thread(() -> handleClient(clientSocket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleClient(Socket clientSocket) {
try (clientSocket;
InputStream in = clientSocket.getInputStream();
OutputStream out = clientSocket.getOutputStream()) {
// 读取请求
byte[] requestData = readRequest(in);
String request = new String(requestData);
// 解析请求
String[] lines = request.split("\r\n");
String[] parts = lines[0].split(" ");
String method = parts[0];
String url = parts[1];
// 只处理GET请求的缓存
if (method.equals("GET")) {
// 检查缓存
if (cache.containsKey(url)) {
CacheEntry entry = cache.get(url);
if (!entry.isExpired()) {
System.out.println("命中缓存: " + url + " 缓存大小: " + entry.data.length);
out.write(entry.data);
return;
} else {
cache.remove(url);
}
}
// 转发请求并从目标服务器获取响应
byte[] response = forwardGetRequest(url);
if (response != null && !requestContainsNoCache(lines)) {
// 缓存响应
cache.put(url, new CacheEntry(response, System.currentTimeMillis()));
System.out.println("缓存响应: " + url + " 大小: " + response.length);
}
out.write(response);
} else {
// 非GET请求直接转发
forwardRequestDirectly(out, requestData, url);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static byte[] readRequest(InputStream in) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] temp = new byte[4096];
int read;
try {
in.setSoTimeout(2000);
while ((read = in.read(temp)) != -1) {
buffer.write(temp, 0, read);
if (buffer.size() > 100000) break; // 限制请求大小
}
} catch (SocketTimeoutException e) {
// 请求读取完成
}
return buffer.toByteArray();
}
private static byte[] forwardGetRequest(String url) throws IOException {
URL targetUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) targetUrl.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
int responseCode = conn.getResponseCode();
System.out.println("目标服务器响应: " + responseCode);
if (responseCode >= 200 && responseCode < 300) {
try (InputStream in = conn.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
byte[] temp = new byte[4096];
int read;
while ((read = in.read(temp)) != -1) {
buffer.write(temp, 0, read);
}
// 构建完整的HTTP响应
String headers = "HTTP/1.1 " + responseCode + " " +
conn.getResponseMessage() + "\r\n";
Map<String, List<String>> headerFields = conn.getHeaderFields();
for (Map.Entry<String, List<String>> entry : headerFields.entrySet()) {
if (entry.getKey() != null) {
for (String value : entry.getValue()) {
headers += entry.getKey() + ": " + value + "\r\n";
}
}
}
headers += "\r\n";
byte[] headerBytes = headers.getBytes();
byte[] response = new byte[headerBytes.length + buffer.size()];
System.arraycopy(headerBytes, 0, response, 0, headerBytes.length);
System.arraycopy(buffer.toByteArray(), 0, response, headerBytes.length, buffer.size());
return response;
}
}
return ("HTTP/1.1 " + responseCode + " " +
conn.getResponseMessage() + "\r\n\r\n").getBytes();
}
private static void forwardRequestDirectly(OutputStream clientOut, byte[] requestData,
String url) throws IOException {
URL targetUrl = new URL(url);
int port = targetUrl.getPort() == -1 ? 80 : targetUrl.getPort();
try (Socket targetSocket = new Socket(targetUrl.getHost(), port);
OutputStream targetOut = targetSocket.getOutputStream();
InputStream targetIn = targetSocket.getInputStream()) {
targetOut.write(requestData);
targetOut.flush();
byte[] buffer = new byte[4096];
int read;
while ((read = targetIn.read(buffer)) != -1) {
clientOut.write(buffer, 0, read);
clientOut.flush();
}
}
}
private static boolean requestContainsNoCache(String[] headers) {
for (String header : headers) {
if (header.toLowerCase().contains("no-cache") ||
header.toLowerCase().contains("no-store") ||
header.toLowerCase().contains("max-age=0")) {
return true;
}
}
return false;
}
}
代理服务器配置文件
proxy-config.properties:
# 代理服务器配置 proxy.port=8080 proxy.maxThreads=100 proxy.connectTimeout=5000 proxy.readTimeout=5000 # 缓存配置 cache.enabled=true cache.duration=300000 cache.maxSize=100MB # 日志配置 log.enabled=true log.file=proxy.log
使用说明
运行方式:
# 编译 javac SimpleHttpProxy.java # 运行 java SimpleHttpProxy
测试方法:
# 配置浏览器使用代理 # HTTP Proxy: 127.0.0.1 # Port: 8080 # 或者使用curl测试 curl -x http://127.0.0.1:8080 http://example.com curl -x http://127.0.0.1:8080 https://example.com
关键设计要点
- 线程安全:使用线程池管理并发连接
- 异常处理:所有网络操作都需要异常处理
- 缓冲区管理:合理的缓冲区大小至关重要
- 超时设置:防止代理服务器挂起
- 资源管理:使用try-with-resources确保连接关闭
这些案例覆盖了大多数代理服务器的基本需求,可以根据实际场景选择合适的实现方式并进一步扩展功能。