Java 应用如何防御 SYN Flood 攻击?

231 阅读17分钟

SYN Flood 是一种常见的 DDoS 攻击方式,通过发送大量伪造的 TCP 连接请求耗尽服务器资源。本文将深入探讨其原理,并提供 Java 应用层面的防御方案。

SYN Flood 攻击原理

TCP 三次握手过程中,攻击者只发送 SYN 包但不完成后续握手,导致服务器维护大量半开连接,最终资源耗尽无法响应正常请求。

SYNFlood攻击原理.png

SYN Cookie 原理

SYN Cookie 是一种无状态的防御机制,服务器不保存半开连接信息,而是将信息编码在 SYN-ACK 的序列号中:

  1. 收到 SYN 时,计算一个特殊的序列号(cookie)
  2. 将连接信息编码到这个序列号中
  3. 收到 ACK 时,从序列号中解码出原始信息
  4. 验证通过后才建立连接

这样可以完全避免半开连接队列被耗尽的问题。

Java 应用防御策略

1. 系统优化器

import java.io.*;
import java.util.logging.*;

public class SystemOptimizer {
    private static final Logger logger = Logger.getLogger(SystemOptimizer.class.getName());

    public static void printOptimizationGuide() {
        logger.info("=== Linux系统优化建议 ===");
        logger.info("1. 增加半连接队列大小:");
        logger.info("   echo 2048 > /proc/sys/net/ipv4/tcp_max_syn_backlog");

        logger.info("2. 减少SYN-ACK重试次数:");
        logger.info("   echo 2 > /proc/sys/net/ipv4/tcp_synack_retries");

        logger.info("3. 启用SYN Cookie:");
        logger.info("   echo 1 > /proc/sys/net/ipv4/tcp_syncookies");

        logger.info("4. 减少TIME_WAIT状态时间:");
        logger.info("   echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout");

        logger.info("5. 增加最大文件描述符:");
        logger.info("   ulimit -n 65535");
    }

    public static void checkSystemConfig() {
        if (!System.getProperty("os.name").toLowerCase().contains("linux")) {
            logger.info("当前系统非Linux,跳过系统配置检查");
            return;
        }

        logger.info("=== 系统配置检查 ===");
        checkSysctl("net.ipv4.tcp_syncookies", "1");
        checkSysctl("net.ipv4.tcp_max_syn_backlog", "2048");
        checkSysctl("net.core.somaxconn", "65535");
        checkSysctl("net.ipv4.tcp_fin_timeout", "30");
        checkSysctl("net.ipv4.tcp_synack_retries", "2");

        checkUlimit();
    }

    private static void checkSysctl(String key, String expectedValue) {
        try {
            Process p = Runtime.getRuntime().exec(new String[]{"sysctl", key});
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(p.getInputStream()))) {
                String line = reader.readLine();
                if (line != null) {
                    String[] parts = line.split("=");
                    if (parts.length == 2) {
                        String actualValue = parts[1].trim();
                        String status = actualValue.equals(expectedValue) ? "✓" : "✗";
                        logger.info(String.format("%s %s = %s (建议值: %s)",
                            status, key, actualValue, expectedValue));

                        if (!actualValue.equals(expectedValue)) {
                            logger.warning(String.format(
                                "%s 当前值 %s 不符合建议值 %s",
                                key, actualValue, expectedValue
                            ));
                        }
                    }
                }
            }
            p.waitFor();
        } catch (IOException | InterruptedException e) {
            logger.fine("无法读取系统配置 " + key + ": " + e.getMessage());
        }
    }

    private static void checkUlimit() {
        try {
            Process p = Runtime.getRuntime().exec(new String[]{"bash", "-c", "ulimit -n"});
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(p.getInputStream()))) {
                String line = reader.readLine();
                if (line != null) {
                    int limit = Integer.parseInt(line.trim());
                    String status = limit >= 65535 ? "✓" : "✗";
                    logger.info(String.format("%s 文件描述符限制: %d (建议值: ≥65535)",
                        status, limit));

                    if (limit < 65535) {
                        logger.warning("文件描述符限制过低: " + limit);
                    }
                }
            }
            p.waitFor();
        } catch (Exception e) {
            logger.fine("无法检查文件描述符限制: " + e.getMessage());
        }
    }
}

2. 配置管理中心

import java.util.*;
import java.io.*;
import java.util.logging.*;

public class DefenseConfig {
    private static final Logger logger = Logger.getLogger(DefenseConfig.class.getName());
    private static final Properties config = new Properties();
    private static final String CONFIG_FILE = "/defense.properties";

    static {
        loadConfig();
    }

    public static void loadConfig() {
        config.clear();
        loadConfigFromFile();
        loadConfigFromEnv();
    }

    private static void loadConfigFromFile() {
        try (InputStream is = DefenseConfig.class.getResourceAsStream(CONFIG_FILE)) {
            if (is != null) {
                config.load(is);
                logger.info("配置文件加载成功");
            }
        } catch (IOException e) {
            logger.warning("配置文件加载失败,使用默认值: " + e.getMessage());
        }
    }

    private static void loadConfigFromEnv() {
        Map<String, String> env = System.getenv();
        env.forEach((key, value) -> {
            if (key.startsWith("DEFENSE_")) {
                String propKey = key.substring(8).toLowerCase().replace('_', '.');
                config.setProperty(propKey, value);
            }
        });
    }

    public static int getMaxConnections() {
        return Integer.parseInt(config.getProperty("max.connections", "1000"));
    }

    public static int getMaxConnectionsPerIp() {
        return Integer.parseInt(config.getProperty("max.connections.per.ip", "10"));
    }

    public static int getRateLimitCapacity() {
        return Integer.parseInt(config.getProperty("rate.limit.capacity", "100"));
    }

    public static double getRateLimitRefillRate() {
        return Double.parseDouble(config.getProperty("rate.limit.refill.rate", "50"));
    }

    public static int getSuspiciousThreshold() {
        return Integer.parseInt(config.getProperty("suspicious.threshold", "5"));
    }

    public static int getServerPort() {
        return Integer.parseInt(config.getProperty("server.port", "8080"));
    }

    public static int getAdminPort() {
        return Integer.parseInt(config.getProperty("admin.port", "8081"));
    }

    public static int getSocketTimeout() {
        return Integer.parseInt(config.getProperty("socket.timeout", "30000"));
    }

    // 配置验证
    public static void validate() {
        int serverPort = getServerPort();
        if (serverPort < 1024 || serverPort > 65535) {
            throw new IllegalArgumentException("服务端口必须在1024-65535之间");
        }

        int maxConnections = getMaxConnections();
        int maxPerIp = getMaxConnectionsPerIp();
        if (maxPerIp > maxConnections) {
            throw new IllegalArgumentException("单IP最大连接数不能超过总连接数");
        }

        double refillRate = getRateLimitRefillRate();
        if (refillRate <= 0) {
            throw new IllegalArgumentException("令牌补充速率必须大于0");
        }
    }
}

3. 连接数限制器

import java.util.concurrent.*;
import java.net.*;
import java.io.*;
import java.util.logging.*;

public class ConnectionLimiter {
    private static final Logger logger = Logger.getLogger(ConnectionLimiter.class.getName());

    private final Semaphore connectionSemaphore;
    private final int maxConnections;
    private final ConcurrentHashMap<String, AtomicInteger> ipConnectionCount;
    private final int maxConnectionsPerIp;
    private final ScheduledExecutorService cleanupExecutor;

    public ConnectionLimiter(int maxConnections, int maxConnectionsPerIp) {
        this.maxConnections = maxConnections;
        this.maxConnectionsPerIp = maxConnectionsPerIp;
        this.connectionSemaphore = new Semaphore(maxConnections);
        this.ipConnectionCount = new ConcurrentHashMap<>();
        this.cleanupExecutor = Executors.newScheduledThreadPool(1);

        // 定期清理无连接的IP记录
        cleanupExecutor.scheduleAtFixedRate(this::cleanup, 1, 1, TimeUnit.MINUTES);
    }

    public boolean acceptConnection(Socket socket) throws IOException {
        String clientIp = socket.getInetAddress().getHostAddress();

        // 检查单个IP的连接数
        AtomicInteger count = ipConnectionCount.computeIfAbsent(
            clientIp, k -> new AtomicInteger(0)
        );

        if (count.get() >= maxConnectionsPerIp) {
            logger.warning("IP " + clientIp + " 超过最大连接数限制");
            socket.close();
            return false;
        }

        // 检查总连接数
        if (!connectionSemaphore.tryAcquire()) {
            logger.warning("全局连接数已达上限");
            socket.close();
            return false;
        }

        count.incrementAndGet();
        return true;
    }

    public void releaseConnection(Socket socket) {
        String clientIp = socket.getInetAddress().getHostAddress();
        connectionSemaphore.release();

        ipConnectionCount.computeIfPresent(clientIp, (k, v) -> {
            int newCount = v.decrementAndGet();
            return newCount > 0 ? v : null;
        });
    }

    public int getCurrentConnections() {
        return maxConnections - connectionSemaphore.availablePermits();
    }

    private void cleanup() {
        ipConnectionCount.entrySet().removeIf(entry ->
            entry.getValue().get() == 0
        );
    }

    public void shutdown() {
        cleanupExecutor.shutdown();
    }
}

4. 分布式限流器

import redis.clients.jedis.*;
import java.util.concurrent.*;
import java.util.logging.*;

public class DistributedRateLimiter {
    private static final Logger logger = Logger.getLogger(DistributedRateLimiter.class.getName());

    private final JedisPool jedisPool;
    private final int maxRequestsPerSecond;
    private final int maxRequestsPerIpPerSecond;

    public DistributedRateLimiter(String redisHost, int redisPort,
                                  int maxRequestsPerSecond,
                                  int maxRequestsPerIpPerSecond) {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(100);
        config.setMaxIdle(50);
        config.setMinIdle(10);
        config.setTestOnBorrow(true);
        config.setTestWhileIdle(true);
        config.setTimeBetweenEvictionRunsMillis(30000);
        config.setMinEvictableIdleTimeMillis(60000);

        this.jedisPool = new JedisPool(config, redisHost, redisPort, 2000, 2000);
        this.maxRequestsPerSecond = maxRequestsPerSecond;
        this.maxRequestsPerIpPerSecond = maxRequestsPerIpPerSecond;
    }

    public boolean tryAcquire(String clientIp) {
        try (Jedis jedis = jedisPool.getResource()) {
            // 检查全局限流
            String globalKey = "rate_limit:global";
            if (!checkRateLimit(jedis, globalKey, maxRequestsPerSecond)) {
                return false;
            }

            // 检查IP级别限流
            String ipKey = "rate_limit:ip:" + clientIp;
            return checkRateLimit(jedis, ipKey, maxRequestsPerIpPerSecond);
        }
    }

    private boolean checkRateLimit(Jedis jedis, String key, int maxRequests) {
        long currentTime = System.currentTimeMillis();
        long windowStart = currentTime - 1000;

        Transaction tx = jedis.multi();

        // 清理过期记录
        tx.zremrangeByScore(key, 0, windowStart);

        // 添加当前请求
        tx.zadd(key, currentTime, String.valueOf(currentTime));

        // 设置过期时间
        tx.expire(key, 2);

        // 获取窗口内的请求数
        Response<Long> count = tx.zcard(key);

        tx.exec();

        return count.get() <= maxRequests;
    }

    public void shutdown() {
        jedisPool.close();
    }
}

// 混合限流器:Redis挂了自动降级到本地
public class HybridRateLimiter {
    private static final Logger logger = Logger.getLogger(HybridRateLimiter.class.getName());

    private final DistributedRateLimiter distributedLimiter;
    private final RateLimiter localLimiter;
    private volatile boolean useDistributed = true;

    public HybridRateLimiter(DistributedRateLimiter distributedLimiter,
                            RateLimiter localLimiter) {
        this.distributedLimiter = distributedLimiter;
        this.localLimiter = localLimiter;
    }

    public boolean tryAcquire(String clientIp) {
        if (useDistributed) {
            try {
                return distributedLimiter.tryAcquire(clientIp);
            } catch (Exception e) {
                logger.warning("Redis不可用,降级到本地限流: " + e.getMessage());
                useDistributed = false;
                scheduleRedisCheck();
            }
        }
        return localLimiter.tryAcquire(clientIp);
    }

    private void scheduleRedisCheck() {
        CompletableFuture.delayedExecutor(30, TimeUnit.SECONDS)
            .execute(() -> {
                useDistributed = true;
                logger.info("尝试恢复Redis限流");
            });
    }
}

5. 本地限流器

import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;

public class RateLimiter {
    private static final Logger logger = Logger.getLogger(RateLimiter.class.getName());

    private final long capacity;
    private final double refillRate;
    private final AtomicLong tokens;
    private final AtomicLong lastRefillTime;
    private final ConcurrentHashMap<String, ClientRateLimit> clientLimits;

    public RateLimiter(long capacity, double refillRatePerSecond) {
        this.capacity = capacity;
        this.refillRate = refillRatePerSecond;
        this.tokens = new AtomicLong(capacity);
        this.lastRefillTime = new AtomicLong(System.nanoTime());
        this.clientLimits = new ConcurrentHashMap<>();
    }

    public boolean tryAcquire(String clientIp) {
        // 每个IP单独限流
        ClientRateLimit clientLimit = clientLimits.computeIfAbsent(
            clientIp, k -> new ClientRateLimit(10, 2)
        );

        if (!clientLimit.tryAcquire()) {
            return false;
        }

        // 全局限流
        refillTokens();
        return tokens.getAndUpdate(current ->
            current > 0 ? current - 1 : current
        ) > 0;
    }

    private void refillTokens() {
        long now = System.nanoTime();
        long lastRefill = lastRefillTime.get();
        long elapsedNanos = now - lastRefill;

        if (elapsedNanos > TimeUnit.MILLISECONDS.toNanos(100) &&
            lastRefillTime.compareAndSet(lastRefill, now)) {
            double elapsedSeconds = elapsedNanos / 1_000_000_000.0;
            long tokensToAdd = (long)(elapsedSeconds * refillRate);

            if (tokensToAdd > 0) {
                tokens.getAndUpdate(current ->
                    Math.min(capacity, current + tokensToAdd)
                );
            }
        }
    }

    static class ClientRateLimit {
        private final long capacity;
        private final double refillRate;
        private final AtomicLong tokens;
        private final AtomicLong lastRefillTime;

        ClientRateLimit(long capacity, double refillRatePerSecond) {
            this.capacity = capacity;
            this.refillRate = refillRatePerSecond;
            this.tokens = new AtomicLong(capacity);
            this.lastRefillTime = new AtomicLong(System.nanoTime());
        }

        boolean tryAcquire() {
            refillTokens();
            return tokens.getAndUpdate(current ->
                current > 0 ? current - 1 : current
            ) > 0;
        }

        private void refillTokens() {
            long now = System.nanoTime();
            long lastRefill = lastRefillTime.get();
            long elapsedNanos = now - lastRefill;

            if (elapsedNanos > TimeUnit.MILLISECONDS.toNanos(100) &&
                lastRefillTime.compareAndSet(lastRefill, now)) {
                double elapsedSeconds = elapsedNanos / 1_000_000_000.0;
                long tokensToAdd = (long)(elapsedSeconds * refillRate);

                if (tokensToAdd > 0) {
                    tokens.getAndUpdate(current ->
                        Math.min(capacity, current + tokensToAdd)
                    );
                }
            }
        }
    }
}

6. IP 黑名单管理

import java.util.*;
import java.util.concurrent.*;
import java.time.*;
import java.util.logging.*;

public class IPBlacklist {
    private static final Logger logger = Logger.getLogger(IPBlacklist.class.getName());

    private final Set<String> permanentBlacklist = ConcurrentHashMap.newKeySet();
    private final ConcurrentHashMap<String, Long> temporaryBlacklist = new ConcurrentHashMap<>();
    private final ScheduledExecutorService cleanupExecutor;

    public IPBlacklist() {
        this.cleanupExecutor = Executors.newScheduledThreadPool(1);
        cleanupExecutor.scheduleAtFixedRate(this::cleanupExpired, 1, 1, TimeUnit.MINUTES);
    }

    public void addToBlacklist(String ip, Duration duration) {
        if (duration == null) {
            permanentBlacklist.add(ip);
            logger.warning("IP永久加入黑名单: " + ip);
        } else {
            temporaryBlacklist.put(ip, System.currentTimeMillis() + duration.toMillis());
            logger.warning("IP临时加入黑名单: " + ip + ", 时长: " + duration);
        }
    }

    public boolean isBlacklisted(String ip) {
        if (permanentBlacklist.contains(ip)) {
            return true;
        }

        Long expiry = temporaryBlacklist.get(ip);
        if (expiry != null) {
            if (System.currentTimeMillis() < expiry) {
                return true;
            } else {
                temporaryBlacklist.remove(ip);
            }
        }

        return false;
    }

    private void cleanupExpired() {
        long now = System.currentTimeMillis();
        temporaryBlacklist.entrySet().removeIf(entry -> entry.getValue() < now);
    }

    public void removeFromBlacklist(String ip) {
        permanentBlacklist.remove(ip);
        temporaryBlacklist.remove(ip);
        logger.info("IP从黑名单移除: " + ip);
    }

    public Set<String> getBlacklistedIPs() {
        Set<String> result = new HashSet<>(permanentBlacklist);
        result.addAll(temporaryBlacklist.keySet());
        return result;
    }

    public void shutdown() {
        cleanupExecutor.shutdown();
    }
}

7. 异常行为检测器

import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.time.*;
import java.util.logging.*;

public class AnomalyDetector {
    private static final Logger logger = Logger.getLogger(AnomalyDetector.class.getName());

    private final ConcurrentHashMap<String, ConnectionMetrics> metricsMap;
    private final ScheduledExecutorService scheduler;
    private final int suspiciousThreshold;
    private final IPBlacklist blacklist;

    public AnomalyDetector(int suspiciousThreshold, IPBlacklist blacklist) {
        this.metricsMap = new ConcurrentHashMap<>();
        this.suspiciousThreshold = suspiciousThreshold;
        this.blacklist = blacklist;
        this.scheduler = Executors.newScheduledThreadPool(1);

        scheduler.scheduleAtFixedRate(this::cleanup, 1, 1, TimeUnit.MINUTES);
    }

    public boolean isSuspicious(String clientIp) {
        if (blacklist.isBlacklisted(clientIp)) {
            return true;
        }

        ConnectionMetrics metrics = metricsMap.computeIfAbsent(
            clientIp, k -> new ConnectionMetrics()
        );

        metrics.recordConnection();

        // 判断是否可疑
        boolean suspicious = metrics.getIncompleteConnections() > suspiciousThreshold ||
                           metrics.getConnectionRate() > 10 ||
                           metrics.getIOExceptionRate() > 0.5;

        if (suspicious) {
            logger.warning(String.format("检测到可疑IP: %s, 未完成连接: %d, 连接速率: %.2f/s",
                clientIp, metrics.getIncompleteConnections(), metrics.getConnectionRate()));

            // 自动拉黑30分钟
            blacklist.addToBlacklist(clientIp, Duration.ofMinutes(30));
        }

        return suspicious;
    }

    public void recordHandshakeComplete(String clientIp) {
        ConnectionMetrics metrics = metricsMap.get(clientIp);
        if (metrics != null) {
            metrics.recordHandshakeComplete();
        }
    }

    public void recordIOException(String clientIp) {
        ConnectionMetrics metrics = metricsMap.get(clientIp);
        if (metrics != null) {
            metrics.recordIOException();
        }
    }

    private void cleanup() {
        Instant cutoff = Instant.now().minus(Duration.ofMinutes(5));
        metricsMap.entrySet().removeIf(entry ->
            entry.getValue().getLastActivity().isBefore(cutoff)
        );
    }

    static class ConnectionMetrics {
        private final AtomicInteger totalConnections = new AtomicInteger(0);
        private final AtomicInteger completedHandshakes = new AtomicInteger(0);
        private final AtomicInteger ioExceptions = new AtomicInteger(0);
        private final AtomicLong lastActivityTime = new AtomicLong(System.currentTimeMillis());
        private final AtomicLong firstConnectionTime = new AtomicLong(0);

        void recordConnection() {
            totalConnections.incrementAndGet();
            lastActivityTime.set(System.currentTimeMillis());
            firstConnectionTime.compareAndSet(0, System.currentTimeMillis());
        }

        void recordHandshakeComplete() {
            completedHandshakes.incrementAndGet();
        }

        void recordIOException() {
            ioExceptions.incrementAndGet();
        }

        int getIncompleteConnections() {
            return totalConnections.get() - completedHandshakes.get();
        }

        double getConnectionRate() {
            long duration = System.currentTimeMillis() - firstConnectionTime.get();
            if (duration == 0) return 0;
            return (double) totalConnections.get() / (duration / 1000.0);
        }

        double getIOExceptionRate() {
            int total = totalConnections.get();
            if (total == 0) return 0;
            return (double) ioExceptions.get() / total;
        }

        Instant getLastActivity() {
            return Instant.ofEpochMilli(lastActivityTime.get());
        }
    }

    public void shutdown() {
        scheduler.shutdown();
    }
}

8. 高性能 NIO 服务器

import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import java.time.Duration;

public class NIOSecureServer {
    private static final Logger logger = Logger.getLogger(NIOSecureServer.class.getName());

    private final int port;
    private final Selector selector;
    private final ServerSocketChannel serverChannel;
    private final ConnectionLimiter connectionLimiter;
    private final HybridRateLimiter rateLimiter;
    private final IPBlacklist blacklist;
    private final AnomalyDetector anomalyDetector;
    private final ExecutorService workerPool;
    private final MetricsCollector metricsCollector;
    private volatile boolean running = true;

    public NIOSecureServer(int port, MetricsCollector metricsCollector) throws IOException {
        this.port = port;
        this.metricsCollector = metricsCollector;
        this.selector = Selector.open();
        this.serverChannel = ServerSocketChannel.open();
        this.serverChannel.bind(new InetSocketAddress(port));
        this.serverChannel.configureBlocking(false);
        this.serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        this.connectionLimiter = new ConnectionLimiter(
            DefenseConfig.getMaxConnections(),
            DefenseConfig.getMaxConnectionsPerIp()
        );

        // 初始化混合限流器
        DistributedRateLimiter distributedLimiter = new DistributedRateLimiter(
            "localhost", 6379,
            DefenseConfig.getRateLimitCapacity(),
            DefenseConfig.getMaxConnectionsPerIp()
        );
        RateLimiter localLimiter = new RateLimiter(
            DefenseConfig.getRateLimitCapacity(),
            DefenseConfig.getRateLimitRefillRate()
        );
        this.rateLimiter = new HybridRateLimiter(distributedLimiter, localLimiter);

        this.blacklist = new IPBlacklist();
        this.anomalyDetector = new AnomalyDetector(
            DefenseConfig.getSuspiciousThreshold(), blacklist);
        this.workerPool = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors() * 2
        );
    }

    public void start() throws IOException {
        logger.info("NIO服务器启动,端口:" + port);

        while (running) {
            selector.select(1000);
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iter = selectedKeys.iterator();

            while (iter.hasNext()) {
                SelectionKey key = iter.next();
                iter.remove();

                try {
                    if (key.isAcceptable()) {
                        handleAccept(key);
                                        } else if (key.isReadable()) {
                        handleRead(key);
                    }
                } catch (Exception e) {
                    logger.severe("处理连接异常: " + e.getMessage());
                    key.cancel();
                    closeChannel(key.channel());
                }
            }
        }
    }

    private void handleAccept(SelectionKey key) throws IOException {
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        SocketChannel clientChannel = serverChannel.accept();

        if (clientChannel == null) return;

        String clientIp = ((InetSocketAddress) clientChannel.getRemoteAddress())
            .getAddress().getHostAddress();

        metricsCollector.recordConnectionAttempt(clientIp);

        if (!performDefenseChecks(clientIp, clientChannel)) {
            return;
        }

        clientChannel.configureBlocking(false);
        clientChannel.register(selector, SelectionKey.OP_READ,
            new ClientContext(clientIp));
    }

    private boolean performDefenseChecks(String clientIp, SocketChannel channel)
            throws IOException {
        // 黑名单检查
        if (blacklist.isBlacklisted(clientIp)) {
            metricsCollector.recordConnectionRejection("blacklist", clientIp);
            closeChannel(channel);
            return false;
        }

        // 异常检测
        if (anomalyDetector.isSuspicious(clientIp)) {
            metricsCollector.recordConnectionRejection("suspicious", clientIp);
            metricsCollector.recordAttackDetection("syn_flood", clientIp);
            closeChannel(channel);
            return false;
        }

        // 速率限制
        if (!rateLimiter.tryAcquire(clientIp)) {
            metricsCollector.recordConnectionRejection("rate_limit", clientIp);
            closeChannel(channel);
            return false;
        }

        // 连接数限制
        Socket socket = channel.socket();
        if (!connectionLimiter.acceptConnection(socket)) {
            metricsCollector.recordConnectionRejection("connection_limit", clientIp);
            return false;
        }

        return true;
    }

    private void handleRead(SelectionKey key) {
        SocketChannel channel = (SocketChannel) key.channel();
        ClientContext context = (ClientContext) key.attachment();

        workerPool.submit(() -> {
            long startTime = System.nanoTime();
            try {
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                int bytesRead = channel.read(buffer);

                if (bytesRead > 0) {
                    buffer.flip();
                    processRequest(channel, buffer, context);
                    metricsCollector.recordRequestDuration(startTime, "process");
                } else if (bytesRead < 0) {
                    closeConnection(key, channel);
                }
            } catch (IOException e) {
                anomalyDetector.recordIOException(context.clientIp);
                closeConnection(key, channel);
            }
        });
    }

    private void processRequest(SocketChannel channel, ByteBuffer buffer,
                               ClientContext context) throws IOException {
        byte[] data = new byte[buffer.remaining()];
        buffer.get(data);
        String request = new String(data);

        // 验证请求合法性
        SecurityEnhancements.InputValidator.ValidationResult validation =
            SecurityEnhancements.InputValidator.validate(request);

        if (!validation.valid) {
            logger.warning("检测到恶意请求,IP: " + context.clientIp + ", 原因: " + validation.reason);
            blacklist.addToBlacklist(context.clientIp, Duration.ofHours(1));
            closeChannel(channel);
            return;
        }

        // 记录握手完成
        anomalyDetector.recordHandshakeComplete(context.clientIp);

        // 简单回显
        String response = "Echo: " + request;
        ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
        channel.write(responseBuffer);
    }

    private void closeConnection(SelectionKey key, SocketChannel channel) {
        key.cancel();
        ClientContext context = (ClientContext) key.attachment();
        if (context != null && channel.socket() != null) {
            connectionLimiter.releaseConnection(channel.socket());
        }
        closeChannel(channel);
    }

    private void closeChannel(Channel channel) {
        try {
            if (channel != null && channel.isOpen()) {
                channel.close();
            }
        } catch (IOException e) {
            // 忽略关闭异常
        }
    }

    public ConnectionLimiter getConnectionLimiter() {
        return connectionLimiter;
    }

    public void stop() {
        running = false;
        workerPool.shutdown();
        connectionLimiter.shutdown();
        blacklist.shutdown();
        anomalyDetector.shutdown();
        try {
            selector.close();
            serverChannel.close();
        } catch (IOException e) {
            logger.severe("关闭服务器异常: " + e.getMessage());
        }
    }

    static class ClientContext {
        final String clientIp;
        final long connectTime;

        ClientContext(String clientIp) {
            this.clientIp = clientIp;
            this.connectTime = System.currentTimeMillis();
        }
    }
}

9. 安全增强模块

import javax.net.ssl.*;
import java.security.*;
import java.security.cert.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.Base64;
import java.util.regex.Pattern;
import java.io.FileInputStream;
import java.util.logging.*;

public class SecurityEnhancements {
    private static final Logger logger = Logger.getLogger(SecurityEnhancements.class.getName());
    private static final String HMAC_ALGORITHM = "HmacSHA256";
    private final SecretKey hmacKey;

    public SecurityEnhancements(String secretKey) {
        this.hmacKey = new SecretKeySpec(secretKey.getBytes(), HMAC_ALGORITHM);
    }

    // 验证请求签名,防止伪造
    public boolean verifyRequestSignature(String request, String signature,
                                         String timestamp) {
        try {
            // 检查时间戳,5分钟内有效
            long requestTime = Long.parseLong(timestamp);
            long currentTime = System.currentTimeMillis();
            if (Math.abs(currentTime - requestTime) > 300000) {
                return false;
            }

            // 计算签名
            Mac mac = Mac.getInstance(HMAC_ALGORITHM);
            mac.init(hmacKey);
            String data = request + timestamp;
            byte[] hmacBytes = mac.doFinal(data.getBytes());
            String calculatedSignature = Base64.getEncoder().encodeToString(hmacBytes);

            return calculatedSignature.equals(signature);
        } catch (Exception e) {
            logger.warning("签名验证失败: " + e.getMessage());
            return false;
        }
    }

    // 创建SSL服务器
    public static SSLServerSocket createSSLServerSocket(int port,
                                                       String keystorePath,
                                                       String keystorePassword)
            throws Exception {
        KeyStore keyStore = KeyStore.getInstance("JKS");
        try (FileInputStream fis = new FileInputStream(keystorePath)) {
            keyStore.load(fis, keystorePassword.toCharArray());
        }

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(keyStore, keystorePassword.toCharArray());

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(keyStore);

        SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
                       new SecureRandom());

        SSLServerSocketFactory factory = sslContext.getServerSocketFactory();
        SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(port);

        // 只允许安全的协议和加密套件
        serverSocket.setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.3"});
        serverSocket.setEnabledCipherSuites(new String[]{
            "TLS_AES_128_GCM_SHA256",
            "TLS_AES_256_GCM_SHA384",
            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
        });

        return serverSocket;
    }

    // 输入验证器
    public static class InputValidator {
        private static final int MAX_REQUEST_SIZE = 4096;
        private static final Pattern SQL_INJECTION_PATTERN = Pattern.compile(
            "(?i).*(union|select|insert|update|delete|drop|create|alter|exec|script).*"
        );
        private static final Pattern XSS_PATTERN = Pattern.compile(
            "(?i).*(<script|javascript:|onerror|onload|onclick).*"
        );

        public static ValidationResult validate(String input) {
            if (input == null || input.isEmpty()) {
                return ValidationResult.invalid("空请求");
            }

            if (input.length() > MAX_REQUEST_SIZE) {
                return ValidationResult.invalid("请求过大");
            }

            if (SQL_INJECTION_PATTERN.matcher(input).matches()) {
                return ValidationResult.invalid("检测到SQL注入");
            }

            if (XSS_PATTERN.matcher(input).matches()) {
                return ValidationResult.invalid("检测到XSS攻击");
            }

            return ValidationResult.valid();
        }

        static class ValidationResult {
            final boolean valid;
            final String reason;

            private ValidationResult(boolean valid, String reason) {
                this.valid = valid;
                this.reason = reason;
            }

            static ValidationResult valid() {
                return new ValidationResult(true, null);
            }

            static ValidationResult invalid(String reason) {
                return new ValidationResult(false, reason);
            }
        }
    }
}

10. 监控指标收集器

实时监控系统状态,及时发现异常:

import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.binder.jvm.*;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.prometheus.*;
import io.prometheus.client.exporter.HTTPServer;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.*;

public class MetricsCollector {
    private static final Logger logger = Logger.getLogger(MetricsCollector.class.getName());

    private final MeterRegistry registry;
    private final Counter connectionAttempts;
    private final Counter connectionRejections;
    private final Timer requestDuration;
    private final Counter attackDetections;
    private ConnectionLimiter connectionLimiter;

    public MetricsCollector() {
        this.registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);

        // 注册JVM指标
        new JvmGcMetrics().bindTo(registry);
        new JvmMemoryMetrics().bindTo(registry);
        new JvmThreadMetrics().bindTo(registry);
        new ProcessorMetrics().bindTo(registry);

        // 自定义指标
        this.connectionAttempts = Counter.builder("connection.attempts")
            .description("总连接尝试数")
            .register(registry);

        this.connectionRejections = Counter.builder("connection.rejections")
            .description("拒绝的连接数")
            .tag("reason", "unknown")
            .register(registry);

        this.requestDuration = Timer.builder("request.duration")
            .description("请求处理时间")
            .publishPercentiles(0.5, 0.95, 0.99)
            .register(registry);

        this.attackDetections = Counter.builder("attack.detections")
            .description("检测到的攻击次数")
            .tag("type", "syn_flood")
            .register(registry);
    }

    public void setConnectionLimiter(ConnectionLimiter connectionLimiter) {
        this.connectionLimiter = connectionLimiter;

        // 注册活跃连接数指标
        Gauge.builder("connections.active", connectionLimiter,
            ConnectionLimiter::getCurrentConnections)
            .description("当前活跃连接数")
            .register(registry);
    }

    public void recordConnectionAttempt(String clientIp) {
        connectionAttempts.increment();

        Counter.builder("connection.attempts.by.ip")
            .tag("ip", anonymizeIp(clientIp))
            .register(registry)
            .increment();
    }

    public void recordConnectionRejection(String reason, String clientIp) {
        Counter.builder("connection.rejections")
            .tag("reason", reason)
            .tag("ip", anonymizeIp(clientIp))
            .register(registry)
            .increment();
    }

    public void recordRequestDuration(long startTime, String endpoint) {
        requestDuration.record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
    }

    public void recordAttackDetection(String attackType, String clientIp) {
        Counter.builder("attack.detections")
            .tag("type", attackType)
            .tag("ip", anonymizeIp(clientIp))
            .register(registry)
            .increment();
    }

    private String anonymizeIp(String ip) {
        // 保护隐私,只显示前两段
        String[] parts = ip.split("\\.");
        if (parts.length == 4) {
            return parts[0] + "." + parts[1] + ".x.x";
        }
        return "unknown";
    }

    public void startHttpServer(int port) throws IOException {
        HTTPServer server = new HTTPServer(port);
        logger.info("Prometheus指标服务启动在端口: " + port);
    }

    public PrometheusMeterRegistry getRegistry() {
        return (PrometheusMeterRegistry) registry;
    }
}

11. 主程序入口

把所有组件组装起来,形成完整的防御系统:

import java.util.concurrent.*;
import java.util.logging.*;
import com.sun.net.httpserver.*;
import java.net.InetSocketAddress;
import java.io.IOException;

public class SynFloodDefenseApplication {
    private static final Logger logger = Logger.getLogger(
        SynFloodDefenseApplication.class.getName());

    private final NIOSecureServer nioServer;
    private final MetricsCollector metricsCollector;
    private final ScheduledExecutorService scheduler;
    private final HttpServer adminServer;

        public SynFloodDefenseApplication() throws Exception {
        // 加载并验证配置
        DefenseConfig.loadConfig();
        DefenseConfig.validate();

        // 初始化监控
        this.metricsCollector = new MetricsCollector();

        // 初始化NIO服务器
        this.nioServer = new NIOSecureServer(DefenseConfig.getServerPort(), metricsCollector);

        // 设置连接限制器到监控系统
        metricsCollector.setConnectionLimiter(nioServer.getConnectionLimiter());

        // 初始化调度器
        this.scheduler = Executors.newScheduledThreadPool(2);

        // 初始化管理接口
        this.adminServer = HttpServer.create(
            new InetSocketAddress(DefenseConfig.getAdminPort()), 0);
        setupAdminEndpoints();
    }

    private void setupAdminEndpoints() {
        // 健康检查
        adminServer.createContext("/health", exchange -> {
            String response = "{\"status\":\"UP\",\"timestamp\":" +
                            System.currentTimeMillis() + "}";
            exchange.getResponseHeaders().set("Content-Type", "application/json");
            exchange.sendResponseHeaders(200, response.length());
            try (var os = exchange.getResponseBody()) {
                os.write(response.getBytes());
            }
        });

        // 配置重载
        adminServer.createContext("/admin/reload-config", exchange -> {
            if (!"POST".equals(exchange.getRequestMethod())) {
                exchange.sendResponseHeaders(405, -1);
                return;
            }

            try {
                DefenseConfig.loadConfig();
                DefenseConfig.validate();
                String response = "{\"status\":\"Config reloaded successfully\"}";
                exchange.sendResponseHeaders(200, response.length());
                try (var os = exchange.getResponseBody()) {
                    os.write(response.getBytes());
                }
                logger.info("配置重新加载成功");
            } catch (Exception e) {
                String error = "{\"error\":\"" + e.getMessage() + "\"}";
                exchange.sendResponseHeaders(500, error.length());
                try (var os = exchange.getResponseBody()) {
                    os.write(error.getBytes());
                }
            }
        });

        adminServer.setExecutor(Executors.newCachedThreadPool());
    }

    public void start() {
        try {
            printStartupInfo();

            // 系统优化检查
            SystemOptimizer.checkSystemConfig();

            // 启动Prometheus
            metricsCollector.startHttpServer(9090);

            // 启动管理服务器
            adminServer.start();
            logger.info("管理接口启动在端口: " + DefenseConfig.getAdminPort());

            // 启动定期任务
            startScheduledTasks();

            // 启动NIO服务器
            Thread serverThread = new Thread(() -> {
                try {
                    nioServer.start();
                } catch (Exception e) {
                    logger.severe("NIO服务器启动失败: " + e.getMessage());
                    e.printStackTrace();
                }
            });
            serverThread.setName("NIOServer-Main");
            serverThread.start();

            logger.info("SYN Flood防御系统启动完成");

            // 注册关闭钩子
            Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));

            // 保持主线程运行
            serverThread.join();

        } catch (Exception e) {
            logger.severe("启动失败: " + e.getMessage());
            e.printStackTrace();
            System.exit(1);
        }
    }

    private void printStartupInfo() {
        String banner = "\n" +
            "███████╗██╗   ██╗███╗   ██╗    ███████╗██╗      ██████╗  ██████╗ ██████╗ \n" +
            "██╔════╝╚██╗ ██╔╝████╗  ██║    ██╔════╝██║     ██╔═══██╗██╔═══██╗██╔══██╗\n" +
            "███████╗ ╚████╔╝ ██╔██╗ ██║    █████╗  ██║     ██║   ██║██║   ██║██║  ██║\n" +
            "╚════██║  ╚██╔╝  ██║╚██╗██║    ██╔══╝  ██║     ██║   ██║██║   ██║██║  ██║\n" +
            "███████║   ██║   ██║ ╚████║    ██║     ███████╗╚██████╔╝╚██████╔╝██████╔╝\n" +
            "╚══════╝   ╚═╝   ╚═╝  ╚═══╝    ╚═╝     ╚══════╝ ╚═════╝  ╚═════╝ ╚═════╝ \n" +
            "                        DEFENSE SYSTEM v1.0                                 \n";

        logger.info(banner);
        logger.info("=== 系统信息 ===");
        logger.info("操作系统: " + System.getProperty("os.name"));
        logger.info("Java版本: " + System.getProperty("java.version"));
        logger.info("可用处理器: " + Runtime.getRuntime().availableProcessors());
        logger.info("最大内存: " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
        logger.info("服务端口: " + DefenseConfig.getServerPort());
        logger.info("管理端口: " + DefenseConfig.getAdminPort());
        logger.info("监控端口: 9090");
    }

    private void startScheduledTasks() {
        // 定期打印统计信息
        scheduler.scheduleAtFixedRate(() -> {
            try {
                printStatistics();
            } catch (Exception e) {
                logger.warning("打印统计信息失败: " + e.getMessage());
            }
        }, 1, 1, TimeUnit.MINUTES);

        // 定期检查系统健康
        scheduler.scheduleAtFixedRate(() -> {
            try {
                checkSystemHealth();
            } catch (Exception e) {
                logger.warning("系统健康检查失败: " + e.getMessage());
            }
        }, 30, 30, TimeUnit.SECONDS);
    }

    private void printStatistics() {
        logger.info("=== 运行统计 ===");
        logger.info("活跃连接数: " + nioServer.getConnectionLimiter().getCurrentConnections());
        logger.info("系统运行正常");
    }

    private void checkSystemHealth() {
        Runtime runtime = Runtime.getRuntime();
        long usedMemory = runtime.totalMemory() - runtime.freeMemory();
        double memoryUsage = (double) usedMemory / runtime.maxMemory();

        if (memoryUsage > 0.9) {
            logger.warning("内存使用率过高: " +
                String.format("%.2f%%", memoryUsage * 100));
        }

        int threadCount = Thread.activeCount();
        if (threadCount > 1000) {
            logger.warning("线程数过多: " + threadCount);
        }
    }

    private void shutdown() {
        logger.info("正在关闭SYN Flood防御系统...");

        try {
            nioServer.stop();
            scheduler.shutdown();
            scheduler.awaitTermination(5, TimeUnit.SECONDS);
            adminServer.stop(0);

            logger.info("系统已安全关闭");
        } catch (Exception e) {
            logger.severe("关闭过程中发生错误: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        try {
            // 配置日志
            LogManager.getLogManager().readConfiguration(
                SynFloodDefenseApplication.class
                    .getResourceAsStream("/logging.properties"));

            // 启动应用
            SynFloodDefenseApplication app = new SynFloodDefenseApplication();
            app.start();

        } catch (Exception e) {
            System.err.println("启动失败: " + e.getMessage());
            e.printStackTrace();
            System.exit(1);
        }
    }
}

配置文件示例

defense.properties

# 连接限制配置
max.connections=1000
max.connections.per.ip=10

# 速率限制配置
rate.limit.capacity=100
rate.limit.refill.rate=50

# 异常检测配置
suspicious.threshold=5

# 服务器配置
server.port=8080
admin.port=8081
socket.timeout=30000

logging.properties

# 日志配置
handlers=java.util.logging.ConsoleHandler,java.util.logging.FileHandler

# 默认日志级别
.level=INFO

# 控制台处理器
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %2$s %5$s%6$s%n

# 文件处理器
java.util.logging.FileHandler.level=ALL
java.util.logging.FileHandler.pattern=logs/application.log
java.util.logging.FileHandler.limit=10485760
java.util.logging.FileHandler.count=10
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append=true

# 特定包的日志级别
com.defense.level=FINE

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.defense</groupId>
    <artifactId>syn-flood-defense</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Redis客户端 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.3.1</version>
        </dependency>

        <!-- Prometheus监控 -->
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
            <version>1.10.5</version>
        </dependency>

        <!-- 测试框架 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>5.1.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.5.0</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

测试用例

import org.junit.*;
import org.mockito.*;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import java.net.*;
import java.util.concurrent.*;
import java.time.*;

public class DefenseSystemTest {

    @Mock
    private Socket mockSocket;

    @Mock
    private InetAddress mockAddress;

    private ConnectionLimiter connectionLimiter;
    private RateLimiter rateLimiter;
    private IPBlacklist blacklist;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        connectionLimiter = new ConnectionLimiter(100, 5);
        rateLimiter = new RateLimiter(10, 5);
        blacklist = new IPBlacklist();

        // 配置mock
        when(mockAddress.getHostAddress()).thenReturn("192.168.1.100");
        when(mockSocket.getInetAddress()).thenReturn(mockAddress);
        when(mockSocket.isClosed()).thenReturn(false);
    }

    @Test
    public void testRateLimiterAccuracy() throws InterruptedException {
        String testIp = "192.168.1.100";
        int successCount = 0;

        // 消耗初始令牌
        for (int i = 0; i < 10; i++) {
            assertTrue(rateLimiter.tryAcquire(testIp));
        }
        assertFalse(rateLimiter.tryAcquire(testIp));

        // 等待补充
        Thread.sleep(1100);

        // 应该补充了约5个令牌
        for (int i = 0; i < 10; i++) {
            if (rateLimiter.tryAcquire(testIp)) {
                successCount++;
            }
        }

        assertTrue(successCount >= 4 && successCount <= 6);
    }

    @Test
    public void testBlacklistExpiration() throws InterruptedException {
        String testIp = "192.168.1.100";

        // 添加临时黑名单
        blacklist.addToBlacklist(testIp, Duration.ofMillis(500));
        assertTrue(blacklist.isBlacklisted(testIp));
                // 等待过期
        Thread.sleep(600);
        assertFalse(blacklist.isBlacklisted(testIp));
    }

    @Test
    public void testConnectionLimiterPerIP() throws Exception {
        String testIp = "192.168.1.100";

        // 测试单IP连接限制
        for (int i = 0; i < 5; i++) {
            Socket socket = mock(Socket.class);
            InetAddress address = mock(InetAddress.class);

            when(address.getHostAddress()).thenReturn(testIp);
            when(socket.getInetAddress()).thenReturn(address);
            when(socket.isClosed()).thenReturn(false);

            assertTrue("第" + (i+1) + "个连接应该成功",
                connectionLimiter.acceptConnection(socket));
        }

        // 第6个连接应该失败
        Socket socket = mock(Socket.class);
        InetAddress address = mock(InetAddress.class);

        when(address.getHostAddress()).thenReturn(testIp);
        when(socket.getInetAddress()).thenReturn(address);
        when(socket.isClosed()).thenReturn(false);

        assertFalse("第6个连接应该失败",
            connectionLimiter.acceptConnection(socket));

        // 验证socket被关闭
        verify(socket).close();
    }

    @After
    public void tearDown() {
        connectionLimiter.shutdown();
        blacklist.shutdown();
    }
}

性能测试工具

import java.net.*;
import java.io.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;

public class PerformanceTest {
    private static final Logger logger = Logger.getLogger(PerformanceTest.class.getName());

    public static void main(String[] args) throws Exception {
        int threads = 100;
        int requestsPerThread = 1000;
        String targetHost = "localhost";
        int targetPort = 8080;

        ExecutorService executor = Executors.newFixedThreadPool(threads);
        CountDownLatch startLatch = new CountDownLatch(1);
        CountDownLatch endLatch = new CountDownLatch(threads);

        AtomicInteger successCount = new AtomicInteger();
        AtomicInteger failCount = new AtomicInteger();
        AtomicLong totalLatency = new AtomicLong();

        for (int i = 0; i < threads; i++) {
            executor.submit(() -> {
                try {
                    startLatch.await();

                    for (int j = 0; j < requestsPerThread; j++) {
                        long start = System.nanoTime();

                        try (Socket socket = new Socket(targetHost, targetPort)) {
                            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
                            BufferedReader in = new BufferedReader(
                                new InputStreamReader(socket.getInputStream()));

                            out.println("TEST");
                            String response = in.readLine();

                            if (response != null) {
                                successCount.incrementAndGet();
                                totalLatency.addAndGet(System.nanoTime() - start);
                            } else {
                                failCount.incrementAndGet();
                            }
                        } catch (Exception e) {
                            failCount.incrementAndGet();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    endLatch.countDown();
                }
            });
        }

        logger.info("开始性能测试...");
        long testStart = System.currentTimeMillis();
        startLatch.countDown();

        endLatch.await();
        long testDuration = System.currentTimeMillis() - testStart;

        logger.info("=== 测试结果 ===");
        logger.info(String.format("总请求数: %d", threads * requestsPerThread));
        logger.info(String.format("成功: %d", successCount.get()));
        logger.info(String.format("失败: %d", failCount.get()));
        logger.info(String.format("总耗时: %.2f秒", testDuration / 1000.0));
        logger.info(String.format("吞吐量: %.2f req/s",
            successCount.get() / (testDuration / 1000.0)));
        logger.info(String.format("平均延迟: %.2f ms",
            totalLatency.get() / successCount.get() / 1_000_000.0));

        executor.shutdown();
    }
}

容器化部署

Dockerfile

# 构建阶段
FROM maven:3.8-openjdk-11 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests

# 运行阶段
FROM openjdk:11-jre-slim
WORKDIR /app

# 安装必要工具
RUN apt-get update && apt-get install -y \
    curl \
    netcat \
    && rm -rf /var/lib/apt/lists/*

# 复制打包好的文件
COPY --from=builder /app/target/syn-flood-defense.jar .
COPY --from=builder /app/target/lib ./lib

# 复制配置文件
COPY config/defense.properties ./config/

# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
RUN chown -R appuser:appuser /app
USER appuser

# JVM优化参数
ENV JAVA_OPTS="-XX:+UseG1GC \
    -XX:MaxGCPauseMillis=100 \
    -XX:+UseStringDeduplication \
    -XX:+ParallelRefProcEnabled \
    -XX:MaxRAMPercentage=75.0 \
    -XX:+ExitOnOutOfMemoryError \
    -XX:+HeapDumpOnOutOfMemoryError \
    -XX:HeapDumpPath=/tmp/heapdump.hprof"

# 暴露端口
EXPOSE 8080 8081 9090

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
    CMD curl -f http://localhost:8081/health || exit 1

# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar syn-flood-defense.jar"]

Kubernetes 部署配置

apiVersion: apps/v1
kind: Deployment
metadata:
  name: syn-flood-defense
spec:
  replicas: 3
  selector:
    matchLabels:
      app: syn-flood-defense
  template:
    metadata:
      labels:
        app: syn-flood-defense
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9090"
        prometheus.io/path: "/metrics"
    spec:
      containers:
      - name: app
        image: syn-flood-defense:latest
        ports:
        - containerPort: 8080
          name: service
        - containerPort: 8081
          name: admin
        - containerPort: 9090
          name: metrics
        env:
        - name: DEFENSE_MAX_CONNECTIONS
          value: "2000"
        - name: REDIS_HOST
          value: redis-service
        resources:
          requests:
            memory: "1Gi"
            cpu: "500m"
          limits:
            memory: "2Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8081
          initialDelaySeconds: 30
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: syn-flood-defense-service
spec:
  selector:
    app: syn-flood-defense
  ports:
  - port: 8080
    targetPort: 8080
    name: service
  type: LoadBalancer
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: defense-config
data:
  defense.properties: |
    max.connections=2000
    max.connections.per.ip=20
    rate.limit.capacity=200
    rate.limit.refill.rate=100
    suspicious.threshold=10
    server.port=8080
    socket.timeout=30000

部署运维

快速部署脚本

#!/bin/bash
# deploy.sh - 一键部署脚本

echo "开始部署SYN Flood防御系统..."

# 检查环境
check_environment() {
    echo "检查部署环境..."

    # 检查Java
    if ! command -v java &> /dev/null; then
        echo "错误: 未安装Java"
        exit 1
    fi

    # 检查Docker
    if ! command -v docker &> /dev/null; then
        echo "警告: 未安装Docker,跳过容器化部署"
    fi

    echo "环境检查通过"
}

# 创建应用用户
create_app_user() {
    if ! id "appuser" &>/dev/null; then
        echo "创建应用用户..."
        sudo useradd -r -s /bin/false appuser
    fi
}

# 优化系统参数
optimize_system() {
    echo "优化系统参数..."

    # 备份原配置
    sudo cp /etc/sysctl.conf /etc/sysctl.conf.backup

    # 应用优化
    cat << EOF | sudo tee -a /etc/sysctl.conf
# SYN Flood Defense
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_synack_retries = 2
net.core.somaxconn = 65535
EOF

    sudo sysctl -p
    echo "系统优化完成"
}

# 部署应用
deploy_application() {
    echo "部署应用..."

    # 创建目录
    sudo mkdir -p /opt/syn-flood-defense/{config,logs}

    # 复制文件
    sudo cp target/syn-flood-defense.jar /opt/syn-flood-defense/
    sudo cp config/* /opt/syn-flood-defense/config/

    # 设置权限
    sudo chown -R appuser:appuser /opt/syn-flood-defense

    # 创建systemd服务
    cat << EOF | sudo tee /etc/systemd/system/syn-flood-defense.service
[Unit]
Description=SYN Flood Defense System
After=network.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/syn-flood-defense
ExecStart=/usr/bin/java -jar syn-flood-defense.jar
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

    # 启动服务
    sudo systemctl daemon-reload
    sudo systemctl enable syn-flood-defense
    sudo systemctl start syn-flood-defense

    echo "应用部署完成"
}

# 执行部署
check_environment
create_app_user
optimize_system
deploy_application

echo "部署完成!"
echo "服务状态: sudo systemctl status syn-flood-defense"
echo "查看日志: sudo journalctl -u syn-flood-defense -f"

Prometheus 告警规则

# prometheus-alerts.yml
groups:
- name: syn_flood_alerts
  rules:
  - alert: HighConnectionRejectionRate
    expr: rate(connection_rejections[1m]) > 100
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: "连接拒绝率过高"
      description: "过去1分钟拒绝了 {{ $value }} 个连接"

  - alert: PossibleSynFloodAttack
    expr: rate(attack_detections{type="syn_flood"}[5m]) > 10
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "检测到可能的SYN Flood攻击"
      description: "5分钟内检测到 {{ $value }} 次攻击"

  - alert: HighMemoryUsage
    expr: jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} > 0.9
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "JVM内存使用率过高"
      description: "堆内存使用率超过90%"

总结

防御措施实现方式效果适用场景
连接数限制Semaphore + ConcurrentHashMap防止资源耗尽所有 TCP 服务
分布式限流Redis + 滑动窗口支持集群部署分布式系统
本地限流令牌桶算法平滑限流单机应用
IP 黑名单永久/临时黑名单快速阻断恶意 IP遭受持续攻击时
异常检测行为模式分析自动识别攻击智能防御场景
NIO 服务器Java NIO + 线程池高并发处理高性能要求
SSL/TLSTLSv1.3 加密传输安全敏感数据传输
输入验证正则匹配防止注入攻击Web 应用
监控告警Prometheus + Grafana实时监控生产环境
系统优化内核参数调整提升抗压能力Linux 服务器

附录:API 文档

/**
 * 管理接口API文档
 *
 * GET /health
 * 健康检查接口
 * 响应示例:
 * {
 *   "status": "UP",
 *   "timestamp": 1234567890,
 *   "checks": {
 *     "redis": "UP",
 *     "memory": "OK",
 *     "connections": 150
 *   }
 * }
 *
 * POST /admin/reload-config
 * 重新加载配置文件
 * 响应示例:
 * {
 *   "status": "Config reloaded successfully"
 * }
 *
 * GET /admin/blacklist
 * 获取当前黑名单列表
 * 响应示例:
 * {
 *   "blacklist": [
 *     {"ip": "192.168.1.100", "type": "permanent"},
 *     {"ip": "192.168.1.101", "type": "temporary", "expiry": 1234567890}
 *   ]
 * }
 *
 * POST /admin/blacklist
 * 添加IP到黑名单
 * 请求体:
 * {
 *   "ip": "192.168.1.100",
 *   "duration": "30m"  // 可选,不填则永久黑名单
 * }
 *
 * DELETE /admin/blacklist/{ip}
 * 从黑名单移除指定IP
 *
 * GET /metrics
 * Prometheus监控指标
 * 端口:9090
 */

故障排查

遇到问题时,按以下步骤排查:

1. 服务无法启动

# 检查端口占用
netstat -tlnp | grep -E '8080|8081|9090'

# 查看详细日志
tail -f /opt/syn-flood-defense/logs/application.log

# 检查配置文件
cat /opt/syn-flood-defense/config/defense.properties

2. 连接被大量拒绝

# 检查当前配置
curl http://localhost:8081/health

# 查看黑名单
curl http://localhost:8081/admin/blacklist

# 查看监控指标
curl http://localhost:9090/metrics | grep connection

3. 内存占用过高

# 生成堆转储
jmap -dump:format=b,file=heapdump.hprof $(pgrep -f syn-flood-defense)

# 查看线程状态
jstack $(pgrep -f syn-flood-defense) > threaddump.txt

# 查看GC日志
grep "Full GC" /opt/syn-flood-defense/logs/gc.log

4. Redis 连接失败

# 检查Redis状态
redis-cli ping

# 检查连接数
redis-cli info clients

# 测试连接
redis-cli -h localhost -p 6379

5. 性能问题排查

# CPU使用率
top -H -p $(pgrep -f syn-flood-defense)

# 网络连接数
ss -ant | grep :8080 | wc -l

# IO等待
iostat -x 1

性能调优建议

JVM 参数优化

# 生产环境推荐配置
JAVA_OPTS="-server \
  -Xms4g \
  -Xmx4g \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=100 \
  -XX:+ParallelRefProcEnabled \
  -XX:+UseStringDeduplication \
  -XX:+DisableExplicitGC \
  -XX:+AlwaysPreTouch \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseNUMA \
  -Djava.net.preferIPv4Stack=true \
  -Dfile.encoding=UTF-8"

系统参数优化

# 网络优化
echo 'net.core.netdev_max_backlog = 5000' >> /etc/sysctl.conf
echo 'net.ipv4.tcp_mem = 786432 1048576 26777216' >> /etc/sysctl.conf
echo 'net.ipv4.tcp_rmem = 4096 87380 134217728' >> /etc/sysctl.conf
echo 'net.ipv4.tcp_wmem = 4096 65536 134217728' >> /etc/sysctl.conf

# 文件系统优化
echo 'fs.file-max = 2097152' >> /etc/sysctl.conf
echo 'fs.nr_open = 2097152' >> /etc/sysctl.conf

监控优化

# Grafana查询优化
- 使用recording rules减少查询压力
- 合理设置数据保留期
- 使用downsampling降低存储需求

常见问题解答

Q: 为什么选择 NIO 而不是 Netty?

A: NIO 已经能满足大部分场景需求,而且是 JDK 自带的,减少了外部依赖。如果需要更高性能,可以很容易地改造成 Netty 实现。

Q: Redis 挂了怎么办?

A: 系统会自动降级到本地限流,虽然在分布式环境下可能不够精确,但能保证服务可用。建议使用 Redis 集群提高可用性。

Q: 如何确定合适的限流阈值?

A: 先通过压测确定系统极限,然后设置为极限值的 70-80%。生产环境中根据实际情况动态调整。

Q: 黑名单会不会误伤正常用户?

A: 系统采用临时黑名单机制,默认 30 分钟后自动解除。同时提供了 API 可以手动移除误判的 IP。

Q: 能防御多大规模的攻击?

A: 单机能处理每秒数万个连接请求,具体取决于硬件配置。建议配合 CDN 和硬件防火墙使用。