如何在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
相关产品推荐
相关产品推荐

