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

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

SYN Flood 攻击原理

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

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

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

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

Java 应用防御策略

1. 系统优化器

java 复制代码
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. 配置管理中心

java 复制代码
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. 连接数限制器

java 复制代码
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. 分布式限流器

java 复制代码
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. 本地限流器

java 复制代码
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 黑名单管理

java 复制代码
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. 异常行为检测器

java 复制代码
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 服务器

java 复制代码
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. 安全增强模块

java 复制代码
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. 监控指标收集器

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

java 复制代码
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. 主程序入口

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

java 复制代码
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

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

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 复制代码
<?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>

测试用例

java 复制代码
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();
    }
}

性能测试工具

java 复制代码
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

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 部署配置

yaml 复制代码
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

部署运维

快速部署脚本

bash 复制代码
#!/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 告警规则

yaml 复制代码
# 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/TLS TLSv1.3 加密 传输安全 敏感数据传输
输入验证 正则匹配 防止注入攻击 Web 应用
监控告警 Prometheus + Grafana 实时监控 生产环境
系统优化 内核参数调整 提升抗压能力 Linux 服务器

附录:API 文档

java 复制代码
/**
 * 管理接口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. 服务无法启动

bash 复制代码
# 检查端口占用
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. 连接被大量拒绝

bash 复制代码
# 检查当前配置
curl http://localhost:8081/health

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

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

3. 内存占用过高

bash 复制代码
# 生成堆转储
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 连接失败

bash 复制代码
# 检查Redis状态
redis-cli ping

# 检查连接数
redis-cli info clients

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

5. 性能问题排查

bash 复制代码
# CPU使用率
top -H -p $(pgrep -f syn-flood-defense)

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

# IO等待
iostat -x 1

性能调优建议

JVM 参数优化

bash 复制代码
# 生产环境推荐配置
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"

系统参数优化

bash 复制代码
# 网络优化
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

监控优化

yaml 复制代码
# 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 和硬件防火墙使用。

相关推荐
编程、小哥哥44 分钟前
互联网大厂Java求职面试实战:Spring Boot微服务架构及Kafka消息处理示例解析
java·spring boot·微服务·kafka·面试技巧
magic 2452 小时前
return this;返回的是谁
java·开发语言
sg_knight3 小时前
Eureka 高可用集群搭建实战:服务注册与发现的底层原理与避坑指南
java·spring boot·spring·spring cloud·微服务·云原生·eureka
数据潜水员8 小时前
C#基础语法
java·jvm·算法
你这个代码我看不懂8 小时前
Java项目OOM排查
java·开发语言
Zong_09158 小时前
AutoCompose - 携程自动编排【开源】
java·spring boot·开源·自动编排
heart000_19 小时前
MySQL事务与锁机制详解:确保数据一致性的关键【MySQL系列】
数据库·mysql
.生产的驴9 小时前
SpringCloud 分布式锁Redisson锁的重入性与看门狗机制 高并发 可重入
java·分布式·后端·spring·spring cloud·信息可视化·tomcat
一眼青苔9 小时前
MySQL 如何判断某个表中是否存在某个字段
数据库·mysql
虾球xz9 小时前
CppCon 2014 学习:C++ Memory Model Meets High-Update-Rate Data Structures
java·开发语言·c++·学习