意识到 TCP 校验和实际上是一个非常差的校验和,促使我在数据 block 中包含一个额外的校验和 (SHA-256),以验证服务器上数据的完整性,并在损坏的情况下请求数据再次阻止。但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 byteBufferData
}
服务器:
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);
请注意,该代码是测试代码,并不旨在传达任何有用的数据。它仅用于测试数据传输速率。
我有两个问题:
- 也许我不明白某些事情或者做得不正确,但是为什么发送一个字节的数据作为数据接受确认(ACK)对整体数据传输速率影响如此之大?如何避免这种情况?
- SHA-256 足以作为 8kb 大小的数据的校验和吗? (在现有的 TCP CRC 之上)
最佳答案
因为你在等待。假设您和服务器之间有 200 毫秒的延迟。如果没有确认,您将尽快写入数据包,使带宽饱和,然后停止。有了 ack,它看起来像这样:
t=0 send 1st 8k
t=200 server recieves
t=205ish server sends ack
t=405 client recieves ack.
t=410ish client sends 2nd 8k
你浪费了 50% 的发送时间。事实上,我很惊讶它并没有变得更糟。
TCP 有很多功能可以防止此类问题,包括数据滑动窗口(您不会发送一个数据包并确认它,而是发送 N 个数据包,然后服务器确认它收到的数据包,从而允许丢失数据包将按顺序重新发送)。您正在糟糕地重新实现 TCP,而且几乎肯定不应该这样做。
如果您打算这样做,请不要使用 TCP。使用 UDP 或原始套接字并在其上编写新协议(protocol)。您仍在使用 TCP 确认和校验和,因此您的是多余的。
关于java - 发送ACK会大大减慢数据传输速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48115706/