You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中编写具备慢速TCP攻击防御能力的TCP服务器?

防御Java TCP服务器的慢速TCP攻击方案

针对你提到的慢速TCP攻击(恶意客户端以极低速率收发数据耗尽服务器连接资源),结合你提出的「最低数据收发速率检测」思路,这里提供可落地的实现方案,核心是通过主动监控连接活跃度+数据量阈值来替代单纯依赖TCP协议的超时机制:

核心实现思路

我们不为单次读写设置固定超时,而是为每个连接维护两个关键指标:

  • 最后一次数据收发的时间戳
  • 设定时间窗口内(比如30秒)累计收发的数据量
    通过定期检查这两个指标,计算连接的平均数据速率,若低于配置阈值则主动关闭连接。同时配合Socket的setSoTimeout()避免单个读写操作无限挂起。

具体实现步骤

1. 封装客户端连接状态

创建类保存每个客户端连接的状态,方便监控和判断:

public class ClientConnection {
    private final Socket socket;
    private volatile long lastActiveTime;
    private volatile long totalBytesTransferred;
    private final long rateThreshold; // 单位:字节/秒
    private final long checkWindow; // 单位:毫秒

    public ClientConnection(Socket socket, long rateThreshold, long checkWindow) {
        this.socket = socket;
        this.rateThreshold = rateThreshold;
        this.checkWindow = checkWindow;
        this.lastActiveTime = System.currentTimeMillis();
        this.totalBytesTransferred = 0;
    }

    // 更新活跃时间和累计数据量
    public void updateActive(long bytes) {
        this.lastActiveTime = System.currentTimeMillis();
        this.totalBytesTransferred += bytes;
    }

    // 判断是否需要关闭当前连接
    public boolean shouldClose() {
        long elapsed = System.currentTimeMillis() - lastActiveTime;
        if (elapsed > checkWindow) {
            // 窗口内无任何数据交互,直接关闭
            return true;
        }
        // 计算窗口内平均数据速率
        double avgRate = (double) totalBytesTransferred / (elapsed / 1000.0);
        return avgRate < rateThreshold;
    }

    public Socket getSocket() {
        return socket;
    }
}

2. 服务器端连接处理与监控逻辑

在服务器中维护线程安全的连接集合,同时启动定时任务定期清理不符合速率要求的连接:

public class TcpServer {
    private final ServerSocket serverSocket;
    private final ExecutorService workerPool = Executors.newCachedThreadPool();
    private final ScheduledExecutorService monitorPool = Executors.newSingleThreadScheduledExecutor();
    private final ConcurrentHashMap<Socket, ClientConnection> connections = new ConcurrentHashMap<>();
    
    // 配置参数:30秒窗口内至少1KB数据,对应速率约34字节/秒
    private static final long RATE_THRESHOLD = 1024 / 30;
    private static final long CHECK_WINDOW = 30 * 1000;
    private static final int READ_TIMEOUT = 10 * 1000; // 单次读写超时10秒

    public TcpServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
        // 每10秒执行一次连接检查
        monitorPool.scheduleAtFixedRate(this::checkConnections, 0, 10, TimeUnit.SECONDS);
    }

    public void start() {
        while (true) {
            try {
                Socket clientSocket = serverSocket.accept();
                // 设置单次读写超时,避免单个read/write操作无限挂起
                clientSocket.setSoTimeout(READ_TIMEOUT);
                ClientConnection conn = new ClientConnection(clientSocket, RATE_THRESHOLD, CHECK_WINDOW);
                connections.put(clientSocket, conn);
                workerPool.submit(new ClientHandler(conn));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 检查并清理不符合速率要求的连接
    private void checkConnections() {
        for (ClientConnection conn : connections.values()) {
            if (conn.shouldClose()) {
                try {
                    conn.getSocket().close();
                    connections.remove(conn.getSocket());
                } catch (IOException e) {
                    // 忽略关闭异常
                }
            }
        }
    }

    // 客户端连接处理线程
    private class ClientHandler implements Runnable {
        private final ClientConnection conn;

        public ClientHandler(ClientConnection conn) {
            this.conn = conn;
        }

        @Override
        public void run() {
            try (InputStream in = conn.getSocket().getInputStream();
                 OutputStream out = conn.getSocket().getOutputStream()) {
                byte[] buffer = new byte[1024];
                int readBytes;
                while ((readBytes = in.read(buffer)) != -1) {
                    // 示例逻辑:将读取到的数据原封不动返回
                    out.write(buffer, 0, readBytes);
                    out.flush();
                    // 更新连接活跃状态(收发数据量都计入统计)
                    conn.updateActive(readBytes + readBytes);
                }
            } catch (IOException e) {
                // 读写超时或连接异常,直接结束处理
            } finally {
                try {
                    conn.getSocket().close();
                    connections.remove(conn.getSocket());
                } catch (IOException e) {
                    // 忽略关闭异常
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new TcpServer(8080).start();
    }
}

3. 额外优化建议

  • 区分连接阶段:刚建立连接的握手/初始化阶段(比如HTTP请求头传输)可适当放宽速率阈值,避免误杀正常客户端
  • 动态调整阈值:根据服务器当前CPU、活跃连接数等负载情况,动态调整速率阈值和检查窗口
  • 配合系统TCP参数:开启TCP KeepAlive(socket.setKeepAlive(true)),让操作系统辅助检测死连接,和应用层监控形成双重保障
  • 保留连接数限制:继续使用ServerSocket.setBacklog()设置最大等待连接数,避免短时间内大量请求耗尽资源

内容的提问来源于stack exchange,提问作者zhoudu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 20:05:17