You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于ACK致WiFi下TCP传输速率骤降及SHA-256校验有效性的技术咨询

TCP传输中ACK导致性能暴跌及校验和可靠性问题

我在优化TCP数据传输的完整性时,因为觉得TCP校验和的可靠性不足,在每个8KB数据块中额外添加了SHA-256校验和,用于服务器端验证数据完整性,若检测到损坏则请求重传。但添加应用层1字节ACK确认后,WiFi环境下的传输速率从约90Mbps骤降到约12Mbps。

测试代码如下:

客户端代码

SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("192.168.31.30", 3333));
ByteBuffer byteBufferData = ByteBuffer.allocateDirect(1024 * 8);
ByteBuffer byteBufferACK = ByteBuffer.allocateDirect(1);
for (int i = 0; i < 1024; i++) {
    // write data (payload + checksum (SHA-256))
    socketChannel.write(byteBufferData);
    byteBufferData.clear();
    // read ACK
    socketChannel.read(byteBufferACK);
    byteBufferACK.clear();
    // if (byteBufferACK.get() == XXX) // ... retransmission
}

服务器端代码

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(3333));
SocketChannel socketChannel = serverSocketChannel.accept();
ByteBuffer byteBufferData = ByteBuffer.allocateDirect(1024 * 8);
ByteBuffer byteBufferACK = ByteBuffer.allocateDirect(1);
long startTime = System.currentTimeMillis();
while ((socketChannel.read(byteBufferData)) != -1) {
    // when 8192 bytes of data were read
    if (!byteBufferData.hasRemaining()) {
        byteBufferData.clear();
        // write ACK
        socketChannel.write(byteBufferACK);
        byteBufferACK.clear();
    }
}
System.out.println(System.currentTimeMillis() - startTime);

注:上述代码为测试代码,仅用于测试传输速率,无实际业务数据。

现在有两个问题:

  1. 为何仅1字节的ACK确认会对整体传输速率产生如此大的影响?该如何优化?
  2. 在已有TCP校验和的基础上,SHA-256作为8KB数据的校验和是否足够?

回答

问题1:1字节ACK导致速率暴跌的原因及优化方案

原因分析

你现在的实现本质上是应用层的停等协议(Stop-and-Wait):客户端每发送一个8KB的数据块,就必须停下来等待服务器的1字节ACK,收到ACK后才能发送下一个块。

这个模式在WiFi环境下的问题被无限放大:WiFi的往返时间(RTT)通常比有线网络高得多(可能从几毫秒到几十毫秒不等)。假设RTT是10ms,那么每秒最多只能完成100次“发块-等ACK”的循环,每次传输8KB的话,总速率就是 100 * 8KB/s = 800KB/s ≈ 6.4Mbps,和你测得的12Mbps接近(取决于实际RTT)。而之前的90Mbps是TCP利用滑动窗口机制,一次性发送多个数据块,不需要等每个块的ACK就能继续发,充分利用了带宽延迟积。

简单说,你手动实现的停等机制废掉了TCP本身的滑动窗口优势,把高带宽的传输变成了一次次的“请求-响应”小交互,自然速率暴跌。

优化方案

  • 利用TCP原生滑动窗口,去掉应用层停等:TCP本身已经实现了高效的流量控制和确认机制,除非你有特殊的业务需求(比如需要在应用层精准控制重传时机),否则完全不需要在应用层每发一个块就等ACK。让TCP自己处理数据的发送和确认,能回到接近原来的速率。
  • 实现应用层滑动窗口:如果必须保留应用层的ACK校验,可以改为一次性发送N个数据块(比如10个、20个),然后等待对应的ACK,或者采用累积ACK的方式(服务器每收到N个块才发一个ACK)。这样每个RTT可以传输N倍的数据,大幅提升速率。
  • 合并ACK减少交互次数:服务器不要每收到一个8KB块就立即发ACK,而是累积多个块(比如每收到8个块、累计64KB数据量)再发送一次ACK,减少ACK的总数量,降低往返开销。
  • 增大数据块大小:把8KB的块改成更大的尺寸(比如64KB、128KB),这样每个RTT能传输更多数据,同样的RTT次数下总传输量更高,能有效提升速率。
  • 优化校验和计算的性能:SHA-256的计算开销比TCP校验和大很多,这也会占用CPU资源影响传输速率。如果只是检测传输中的比特错误,可以换成更轻量的CRC32,它的错误检测能力足够应对传输错误,且计算速度快得多。

问题2:SHA-256作为8KB数据校验和是否足够?

完全足够,甚至可以说性能过剩了:

  • TCP的校验和是16位的,它的错误漏检率是1/2^16(约1/65536),对于随机比特错误的检测能力有限,尤其是在数据块较大时漏检概率会上升。
  • SHA-256是256位的加密哈希算法,它的碰撞概率几乎为0(从概率学角度来说,要找到两个不同的8KB数据产生相同SHA-256值的可能性微乎其微),错误检测能力远超传输场景的需求。

不过要注意:SHA-256主要设计用于数据完整性验证(防篡改),如果你的需求只是检测传输过程中的随机比特错误,那么用CRC32就完全足够了——它的计算速度比SHA-256快很多,CPU开销小,同时错误检测能力已经能满足传输场景的需求。只有当你需要防止数据被恶意篡改时,SHA-256才是必要的选择。


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

火山引擎 最新活动