TCP 拥塞控制原理及 Java 网络编程最佳应用

203 阅读13分钟

TCP 拥塞控制基本原理

TCP 拥塞控制主要解决网络中的"交通堵塞"问题。当网络中有太多数据包同时传输,就会导致路由器缓冲区溢出,丢包率上升,网络性能下降。

拥塞控制通过以下四个算法来实现:

1. 慢启动算法

慢启动并不"慢",而是指数增长的过程。当 TCP 连接刚建立时,发送方不了解网络状况,所以先发送少量数据探测网络容量。

工作原理:

  • 初始设置拥塞窗口(cwnd)为 1 个 MSS(最大报文段大小,通常为 1460 字节,由 MTU 减去 IP 和 TCP 头部计算得出)
  • 每收到一个 ACK,cwnd 增加 1 个 MSS
  • 结果是 cwnd 呈指数增长:1, 2, 4, 8, 16...
  • 当 cwnd 达到慢启动阈值(ssthresh)时,切换到拥塞避免阶段

慢启动算法.png

简单例子:想象你开车进入一条陌生的公路,不知道限速是多少。你会先慢慢加速,确认道路通畅后再逐渐提速,而不是一开始就踩满油门。

2. 拥塞避免算法

当 cwnd 达到慢启动阈值(ssthresh)或检测到丢包时,TCP 进入拥塞避免阶段,此时窗口增长变为线性。

工作原理:

  • 每个往返时间(RTT)内,cwnd 增加 1 个 MSS
  • 增长公式:cwnd = cwnd + MSS × (MSS/cwnd)
  • 线性增长,避免窗口快速增大导致网络拥塞
  • RTT 是计算拥塞窗口增长的时间基准,当 RTT 增加时,表明网络可能拥塞,应该放慢增长速度

3. 快重传算法

不等超时,提前重传丢失的数据包。

工作原理:

  • 接收方收到失序的数据包时立即发送重复 ACK
  • 发送方收到 3 个重复 ACK 后,认为该包丢失
  • 立即重传丢失的数据包,不等待超时计时器

4. 快恢复算法

发生快重传后,TCP 进入快恢复阶段而不是慢启动。

工作原理:

  • 设置 ssthresh = cwnd / 2
  • 设置 cwnd = ssthresh
  • 直接进入拥塞避免阶段,而非慢启动
  • 避免 cwnd 回到 1,保持较高的传输效率

快恢复算法.png

注意区别:超时触发时进入慢启动(cwnd=1),而快重传触发时进入快恢复(cwnd=ssthresh),这使得网络恢复更快。

Java 网络编程中的 TCP 拥塞控制应用

Java 开发者虽然无法直接控制 TCP 拥塞控制算法核心实现(由操作系统网络栈负责),但可以通过以下方式影响 TCP 连接行为:

  1. 调整 Socket 参数
  2. 使用 JVM 系统属性调整网络行为
  3. 某些平台上可通过 JNI 调用系统 API 调整更底层参数

通过 JVM 属性调整 TCP 参数

// 启动时设置TCP接收缓冲区大小
java -Djava.net.preferIPv4Stack=true -Djava.net.tcp.recvBufferSize=65536 MyApp

// 启用TCP Fast Open(Java 11+)
java -Djdk.net.useFastTcpLoopback=true MyApp

关键 Socket 参数设置

Socket socket = new Socket();
// 设置TCP发送缓冲区大小,建议设为带宽延迟积(BDP)
socket.setSendBufferSize(65536);
// 设置TCP接收缓冲区大小,影响ACK返回效率
socket.setReceiveBufferSize(65536);
// 启用TCP_NODELAY(禁用Nagle算法)
// Nagle算法会合并小数据包减少网络开销,禁用后适用于低延迟场景
socket.setTcpNoDelay(true);
// 设置SO_LINGER,控制连接关闭时数据处理
socket.setSoLinger(true, 10);
// 设置连接超时
socket.connect(new InetSocketAddress("example.com", 80), 5000);

带宽延迟积(BDP)计算方法

带宽延迟积是选择合适缓冲区大小的关键指标:

带宽延迟积(BDP) = 带宽(bits/s) × RTT(s) / 8
例:100Mbps链路,50ms RTT
BDP = 100,000,000 × 0.05 / 8 = 625,000 bytes ≈ 625KB

因此发送/接收缓冲区建议设置为 625KB 左右。

理解缓冲区大小的影响

TCP 拥塞窗口和本地缓冲区大小密切相关。缓冲区太小会限制吞吐量,太大则可能加剧网络拥塞。

以下是一个简单的实验,测试不同缓冲区大小对吞吐量的影响:

public class TCPBufferSizeTest {
    public static void main(String[] args) throws Exception {
        // 创建测试服务器
        ServerSocket serverSocket = new ServerSocket(9999);

        // 客户端线程
        new Thread(() -> {
            try {
                // 等待服务器启动
                Thread.sleep(1000);

                // 测试不同缓冲区大小
                testWithBufferSize(8192);    // 8KB
                testWithBufferSize(65536);   // 64KB
                testWithBufferSize(262144);  // 256KB
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        // 服务器接收数据
        while (true) {
            try (Socket client = serverSocket.accept();
                 InputStream in = client.getInputStream()) {

                byte[] buffer = new byte[4096];
                long totalBytes = 0;
                long startTime = System.currentTimeMillis();

                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
                    totalBytes += bytesRead;
                }

                long duration = System.currentTimeMillis() - startTime;
                System.out.printf("服务器接收完成: %.2f MB/s%n",
                        (totalBytes / 1024.0 / 1024.0) / (duration / 1000.0));
            }
        }
    }

    private static void testWithBufferSize(int bufferSize) throws Exception {
        System.out.println("测试缓冲区大小: " + bufferSize + " bytes");

        Socket socket = new Socket();
        socket.setSendBufferSize(bufferSize);
        socket.connect(new InetSocketAddress("localhost", 9999));

        try (OutputStream out = socket.getOutputStream()) {
            byte[] data = new byte[1024 * 1024]; // 1MB的数据
            Arrays.fill(data, (byte) 'A');

            // 发送10MB数据
            for (int i = 0; i < 10; i++) {
                out.write(data);
            }
        }
        socket.close();

        // 等待下一次测试
        Thread.sleep(2000);
    }
}

实际测试结果示例(本地回环网络,带宽 1Gbps,RTT<1ms):

测试缓冲区大小: 8192 bytes
服务器接收完成: 267.42 MB/s
测试缓冲区大小: 65536 bytes
服务器接收完成: 612.86 MB/s
测试缓冲区大小: 262144 bytes
服务器接收完成: 854.31 MB/s

在高延迟网络环境(100Mbps 带宽,200ms RTT):

测试缓冲区大小: 8192 bytes
服务器接收完成: 5.87 MB/s
测试缓冲区大小: 65536 bytes
服务器接收完成: 42.56 MB/s
测试缓冲区大小: 262144 bytes
服务器接收完成: 78.92 MB/s

处理网络拥塞的 Java 应用层策略

虽然底层的 TCP 拥塞控制由操作系统处理,但我们可以在应用层实现一些策略来配合 TCP 更好地应对网络拥塞:

  1. 自适应数据发送速率
public class AdaptiveDataSender {
    private static final int MSS = 1460; // 标准TCP最大报文段大小
    private int sendWindowSize = MSS * 10; // 初始窗口大小
    private int ssthresh = Integer.MAX_VALUE; // 慢启动阈值
    private long rttEstimate = 100;  // 初始RTT估计(毫秒)
    private Queue<DataPacket> pendingPackets = new LinkedList<>();

    public void addToSendQueue(DataPacket packet) {
        pendingPackets.add(packet);
        trySendPackets();
    }

    private synchronized void trySendPackets() {
        // 根据当前窗口大小发送数据
        int packetsToSend = Math.min(sendWindowSize / MSS, pendingPackets.size());
        for (int i = 0; i < packetsToSend; i++) {
            DataPacket packet = pendingPackets.poll();
            if (packet != null) {
                sendPacket(packet);
            }
        }
    }

    private void sendPacket(DataPacket packet) {
        long sendTime = System.currentTimeMillis();
        packet.setSendTime(sendTime);

        // 异步发送数据
        CompletableFuture.runAsync(() -> {
            // 实际发送逻辑...
            simulateNetworkSend(packet);

            // 收到ACK后更新RTT和窗口大小
            long ackTime = System.currentTimeMillis();
            long packetRtt = ackTime - packet.getSendTime();
            updateRttAndWindow(packetRtt, packet.isSuccess());
        });
    }

    private synchronized void updateRttAndWindow(long packetRtt, boolean success) {
        // 更新RTT估计 (简化的EWMA算法)
        rttEstimate = (long)(0.875 * rttEstimate + 0.125 * packetRtt);

        if (success) {
            // 根据当前窗口大小判断是慢启动还是拥塞避免
            if (sendWindowSize < ssthresh) {
                // 慢启动阶段:指数增长,但设置增长上限
                sendWindowSize = Math.min(sendWindowSize + MSS, ssthresh * 3 / 2);
            } else {
                // 拥塞避免阶段:线性增长,每个RTT增加一个MSS
                sendWindowSize += (MSS * MSS) / sendWindowSize;
            }
        } else {
            // 传输失败,减小窗口和阈值
            ssthresh = Math.max(MSS * 2, sendWindowSize / 2);
            sendWindowSize = Math.max(MSS, sendWindowSize / 2);

            System.out.printf("检测到拥塞: 窗口减小到%d字节, 阈值=%d字节%n",
                sendWindowSize, ssthresh);
        }
    }

    // 模拟网络发送
    private void simulateNetworkSend(DataPacket packet) {
        try {
            // 模拟网络延迟
            Thread.sleep(100 + new Random().nextInt(100));
            // 模拟90%的成功率
            packet.setSuccess(new Random().nextInt(100) < 90);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    // 数据包类
    static class DataPacket {
        private byte[] data;
        private long sendTime;
        private boolean success;

        public DataPacket(byte[] data) {
            this.data = data;
        }

        public void setSendTime(long sendTime) {
            this.sendTime = sendTime;
        }

        public long getSendTime() {
            return sendTime;
        }

        public void setSuccess(boolean success) {
            this.success = success;
        }

        public boolean isSuccess() {
            return success;
        }
    }
}
  1. 使用断路器模式处理网络拥塞
public class NetworkCircuitBreaker {
    private enum State { CLOSED, OPEN, HALF_OPEN }

    private State state = State.CLOSED;
    private int failureThreshold = 5;
    private int failureCount = 0;
    private long openStateTime = 1000; // 开路状态持续时间(毫秒)
    private long lastStateChangeTime;

    public synchronized <T> CompletableFuture<T> execute(Supplier<CompletableFuture<T>> networkCall) {
        if (state == State.OPEN) {
            if (System.currentTimeMillis() - lastStateChangeTime > openStateTime) {
                // 超过开路时间,切换到半开状态
                state = State.HALF_OPEN;
                System.out.println("断路器状态: 半开");
            } else {
                // 仍在开路状态,快速失败
                return CompletableFuture.failedFuture(
                    new RuntimeException("断路器开路中,网络请求被拒绝"));
            }
        }

        // 使用thenCompose避免嵌套的CompletableFuture
        return networkCall.get()
            .thenCompose(result -> {
                handleSuccess();
                return CompletableFuture.completedFuture(result);
            })
            .exceptionally(ex -> {
                handleFailure();
                throw new CompletionException(ex);
            });
    }

    private synchronized void handleSuccess() {
        if (state == State.HALF_OPEN) {
            // 恢复到关闭状态
            state = State.CLOSED;
            failureCount = 0;
            System.out.println("断路器状态: 关闭");
        } else if (state == State.CLOSED) {
            // 重置失败计数
            failureCount = 0;
        }
    }

    private synchronized void handleFailure() {
        if (state == State.CLOSED) {
            failureCount++;
            if (failureCount >= failureThreshold) {
                // 切换到开路状态
                state = State.OPEN;
                lastStateChangeTime = System.currentTimeMillis();
                System.out.println("断路器状态: 开路");
            }
        } else if (state == State.HALF_OPEN) {
            // 半开状态下失败,立即切换到开路状态
            state = State.OPEN;
            lastStateChangeTime = System.currentTimeMillis();
            System.out.println("断路器状态: 开路");
        }
    }
}
  1. 实现指数退避重试机制
public class ExponentialBackoffRetry {
    private final int maxRetries;
    private final long initialBackoffMs;
    private final long maxBackoffMs;

    public ExponentialBackoffRetry(int maxRetries, long initialBackoffMs, long maxBackoffMs) {
        this.maxRetries = maxRetries;
        this.initialBackoffMs = initialBackoffMs;
        this.maxBackoffMs = maxBackoffMs;
    }

    public <T> CompletableFuture<T> executeWithRetry(Supplier<CompletableFuture<T>> task) {
        CompletableFuture<T> resultFuture = new CompletableFuture<>();
        retryTask(task, 0, resultFuture);
        return resultFuture;
    }

    private <T> void retryTask(
            Supplier<CompletableFuture<T>> task,
            int currentRetry,
            CompletableFuture<T> resultFuture) {

        if (currentRetry >= maxRetries) {  // 修正重试判断逻辑
            resultFuture.completeExceptionally(
                new RuntimeException("达到最大重试次数: " + maxRetries));
            return;
        }

        task.get().whenComplete((result, error) -> {
            if (error == null) {
                // 成功完成
                resultFuture.complete(result);
            } else {
                // 计算下一次重试的延迟
                long delayMs = calculateBackoff(currentRetry);
                System.out.printf("操作失败,%d毫秒后第%d次重试%n", delayMs, currentRetry + 1);

                // 延迟后重试
                CompletableFuture.delayedExecutor(delayMs, TimeUnit.MILLISECONDS)
                    .execute(() -> retryTask(task, currentRetry + 1, resultFuture));
            }
        });
    }

    private long calculateBackoff(int retry) {
        // 计算指数退避时间:initialBackoffMs * 2^retry
        long backoff = initialBackoffMs * (long)Math.pow(2, retry);
        // 添加随机抖动(0.5~1.5倍),避免大量连接同时重试导致二次拥塞
        double jitter = 0.5 + Math.random();
        backoff = (long)(backoff * jitter);
        // 不超过最大退避时间
        return Math.min(backoff, maxBackoffMs);
    }
}

高级 TCP 拥塞控制算法

除了传统的 TCP Reno 算法外,现代操作系统还实现了更多高级的拥塞控制算法:

不同拥塞控制算法对比

算法核心逻辑适用场景延迟敏感带宽利用率
TCP Reno基于丢包传统网络一般
TCP CUBIC三次函数增长高带宽长延迟网络较差
TCP BBR基于带宽和 RTT数据中心、高可靠网络优秀极高

BBR (Bottleneck Bandwidth and RTT)

BBR 是 Google 开发的新型拥塞控制算法,Linux 4.9+内核已支持。不同于传统基于丢包的算法,BBR 通过估计带宽和 RTT 来控制数据发送。

BBR.png

在 Java 中利用 BBR:

// 在Linux系统上,可以设置使用BBR
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
    try {
        // Linux系统启用BBR
        Runtime.getRuntime().exec("sysctl -w net.ipv4.tcp_congestion_control=bbr");
    } catch (IOException e) {
        // 需要root权限,建议提示用户手动设置
        System.err.println("请手动启用BBR: sysctl -w net.ipv4.tcp_congestion_control=bbr");
    }
} else {
    System.out.println("当前平台(" + System.getProperty("os.name") + ")不支持BBR算法");
}

TCP Fast Open (TFO)

TFO 是一种减少 TCP 连接建立延迟的技术,允许在三次握手期间发送数据。

Java 11+可通过以下系统属性启用 TFO:

-Djdk.net.useFastTcpLoopback=true

使用 Wireshark 分析 TCP 拥塞控制

网络分析工具如 Wireshark 可以帮助我们观察 TCP 拥塞控制的实际工作过程:

典型的 TCP 连接数据包序列:

- 数据包1: SYN发送,客户端初始序列号=1000
- 数据包2: SYN+ACK响应,服务端初始序列号=2000,确认号=1001
- 数据包3: ACK,确认号=2001,连接建立完成
- 数据包4-13: 客户端发送数据,窗口大小逐渐增加(慢启动阶段)
- 数据包14: 服务端发送3个重复ACK,表明丢包
- 数据包15: 客户端快速重传丢失的数据包
- 数据包16+: 窗口大小减半,进入拥塞避免阶段

这种分析能直观展示 TCP 如何根据网络状况调整传输行为。

实战案例:构建支持网络自适应的数据传输服务

下面是一个综合了上述知识点的实战例子,实现了一个能够适应网络条件变化的数据传输服务:

public class AdaptiveNetworkTransfer {
    private static final int DEFAULT_CHUNK_SIZE = 64 * 1024; // 64KB
    private static final int MIN_CHUNK_SIZE = 4 * 1024;      // 4KB
    private static final int MAX_CHUNK_SIZE = 512 * 1024;    // 512KB

    private volatile int currentChunkSize = DEFAULT_CHUNK_SIZE; // 添加volatile保证可见性
    private final ExponentialBackoffRetry retryHandler;
    private final NetworkCircuitBreaker circuitBreaker;
    private final AtomicLong totalBytesSent = new AtomicLong(0);
    private final AtomicLong totalBytesAcked = new AtomicLong(0);
    private final AtomicInteger inFlightChunks = new AtomicInteger(0);
    private final SlidingWindowRttTracker rttTracker = new SlidingWindowRttTracker(20);

    public AdaptiveNetworkTransfer() {
        this.retryHandler = new ExponentialBackoffRetry(3, 100, 5000);
        this.circuitBreaker = new NetworkCircuitBreaker();

        // 启动监控线程
        startMonitoringThread();
    }

    public CompletableFuture<Void> transferFile(Path sourceFile, OutputStream destination)
            throws IOException {

        long fileSize = Files.size(sourceFile);
        int totalChunks = (int) Math.ceil((double) fileSize / currentChunkSize);
        CompletableFuture<Void>[] chunkFutures = new CompletableFuture[totalChunks];

        try (InputStream input = Files.newInputStream(sourceFile)) {
            for (int i = 0; i < totalChunks; i++) {
                final int chunkIndex = i;
                byte[] buffer = new byte[currentChunkSize];
                int bytesRead = input.read(buffer);

                if (bytesRead <= 0) break;

                // 如果不是完整的chunk,调整buffer大小
                if (bytesRead < currentChunkSize) {
                    buffer = Arrays.copyOf(buffer, bytesRead);
                }

                // 记录发送数据
                totalBytesSent.addAndGet(bytesRead);
                inFlightChunks.incrementAndGet();

                // 使用断路器和重试机制发送数据
                chunkFutures[i] = retryHandler.executeWithRetry(() ->
                    circuitBreaker.execute(() -> {
                        long startTime = System.currentTimeMillis();
                        return sendChunk(buffer, destination)
                            .whenComplete((v, e) -> {
                                long rtt = System.currentTimeMillis() - startTime;
                                if (e == null) {
                                    // 成功发送
                                    rttTracker.addRttSample(rtt);
                                    totalBytesAcked.addAndGet(buffer.length);
                                } else {
                                    // 丢包时立即减小数据块大小
                                    synchronized(AdaptiveNetworkTransfer.this) {
                                        currentChunkSize = Math.max(MIN_CHUNK_SIZE, currentChunkSize / 2);
                                        System.out.println("检测到丢包,立即减小块大小到: " + currentChunkSize / 1024 + "KB");
                                    }
                                }
                                inFlightChunks.decrementAndGet();
                            });
                    })
                );
            }
        }

        return CompletableFuture.allOf(chunkFutures);
    }

    private CompletableFuture<Void> sendChunk(byte[] data, OutputStream destination) {
        return CompletableFuture.runAsync(() -> {
            try {
                // 模拟网络延迟和可能的失败
                simulateNetworkConditions();

                // 实际发送数据
                destination.write(data);
                destination.flush();
            } catch (IOException e) {
                throw new CompletionException(e);
            }
        });
    }

    // 监控网络条件并动态调整参数
    private void startMonitoringThread() {
        Thread monitorThread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(1000);
                    adjustParameters();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
        monitorThread.setDaemon(true);
        monitorThread.start();
    }

    private synchronized void adjustParameters() {  // 添加synchronized保证线程安全
        double avgRtt = rttTracker.getAverageRtt();
        int pendingChunks = inFlightChunks.get();
        long bytesDiff = totalBytesSent.get() - totalBytesAcked.get();

        if (avgRtt > 0) {
            // RTT增加,可能有拥塞
            if (avgRtt > 200 || pendingChunks > 10) {
                // 减小数据块大小
                currentChunkSize = Math.max(MIN_CHUNK_SIZE, currentChunkSize / 2);
            }
            // 网络状况良好
            else if (avgRtt < 50 && pendingChunks < 5 && bytesDiff < 1024 * 1024) {
                // 增加数据块大小
                currentChunkSize = Math.min(MAX_CHUNK_SIZE, currentChunkSize * 2);
            }

            // 添加网络状态监控日志
            double sendRate = totalBytesAcked.get() / (System.currentTimeMillis() / 1000.0) / 1024;
            System.out.printf("网络状态: RTT=%.2fms, 未确认块=%d, 当前块大小=%dKB, 发送速率=%.2fKB/s%n",
                    avgRtt, pendingChunks, currentChunkSize/1024, sendRate);
        }
    }

    // 模拟网络条件(仅用于演示)
    private void simulateNetworkConditions() {
        try {
            // 模拟50-150ms的延迟
            Thread.sleep(50 + new Random().nextInt(100));

            // 模拟5%的失败率
            if (new Random().nextInt(100) < 5) {
                throw new IOException("模拟网络错误");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    // RTT追踪器
    static class SlidingWindowRttTracker {
        private final int windowSize;
        private final LinkedList<Long> rttSamples = new LinkedList<>();

        public SlidingWindowRttTracker(int windowSize) {
            this.windowSize = windowSize;
        }

        public synchronized void addRttSample(long rtt) {
            rttSamples.add(rtt);
            if (rttSamples.size() > windowSize) {
                rttSamples.removeFirst();
            }
        }

        public synchronized double getAverageRtt() {
            if (rttSamples.isEmpty()) return 0;

            return rttSamples.stream()
                    .mapToLong(Long::longValue)
                    .average()
                    .orElse(0);
        }
    }
}

TCP vs UDP 拥塞控制对比

值得注意的是,UDP 协议没有内置拥塞控制机制。这意味着:

  1. UDP 可能在拥塞网络中导致"拥塞崩溃"
  2. 需要在应用层实现自定义拥塞控制(如 QUIC 协议)
  3. 当 TCP 和 UDP 流共存时,TCP 流会"礼让",而 UDP 流会获得更多带宽

这就像高速公路上,TCP 车辆会遵守交通规则减速避让,而 UDP 车辆则不管交通状况保持速度。因此在选择网络协议时,应考虑网络环境和拥塞控制需求。

性能测试和优化建议

通过 JMeter 或自定义测试工具,我们可以观察不同网络条件下 TCP 拥塞控制的表现:

性能测试和优化建议.png

优化建议

  1. 适当增大 Socket 缓冲区
  • 长距离、高带宽网络连接受益最大
  • 建议值:带宽延迟积(BDP = 带宽 * RTT)
  • 例:10Mbps 链路,100ms RTT,BDP = 10Mbps * 0.1s = 128KB
  1. 根据应用场景选择 TCP_NODELAY
  • 交互式应用:启用 TCP_NODELAY 减少延迟
  • 大量小数据包:保持默认(Nagle 算法开启)提高吞吐量
  1. 实现应用层流控
  • 监控 RTT 和丢包率
  • 动态调整发送速率和批处理大小
  1. 使用异步 IO 模型
  • 使用 NIO 或 Netty 框架
  • 避免阻塞线程等待网络响应

总结

下面是 TCP 拥塞控制的关键点总结:

机制作用Java 应用层对应策略
慢启动连接初期指数增长窗口,探测网络容量自适应初始发送速率,根据 RTT 动态调整
拥塞避免线性增长窗口,稳定传输监控传输状态,逐步调整发送速率
快重传提前发现丢包并重传实现确认机制,主动检测丢失数据
快恢复丢包后迅速恢复传输指数退避重试,断路器模式
TCP 缓冲区影响吞吐量和延迟根据网络环境调整 Socket 参数
BBR 算法基于带宽和 RTT 的拥塞控制在支持的系统上启用,提高性能
TCP Fast Open减少连接建立延迟启用相应系统参数,提高交互性能