Java代理服务器案例

wen java案例 3

本文目录导读:

Java代理服务器案例

  1. 简单HTTP转发代理
  2. HTTPS CONNECT代理
  3. 轻量级高性能代理(基于NIO)
  4. 带有缓存功能的代理服务器
  5. 代理服务器配置文件
  6. 使用说明
  7. 关键设计要点

我将为您提供几个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

关键设计要点

  1. 线程安全:使用线程池管理并发连接
  2. 异常处理:所有网络操作都需要异常处理
  3. 缓冲区管理:合理的缓冲区大小至关重要
  4. 超时设置:防止代理服务器挂起
  5. 资源管理:使用try-with-resources确保连接关闭

这些案例覆盖了大多数代理服务器的基本需求,可以根据实际场景选择合适的实现方式并进一步扩展功能。

抱歉,评论功能暂时关闭!