如何阻止NetworkStream对TCP数据包进行缓冲?
我需要向服务器发送TCP数据包,但遇到一个问题:多次调用.Write()方法的内容被缓冲合并成一个更大的数据包,尽管MSDN文档指出NetworkStream不会缓冲。
MSDN文档原文:
The Flush method implements the Stream.Flush method; however, because NetworkStream is not buffered, it has no effect on network streams.
(中文翻译:Flush方法实现了Stream.Flush方法;但由于NetworkStream不使用缓冲,因此它对网络流没有任何作用。)
我当前发送数据包的代码如下:
void TestFunction(NetworkStream stream) { var array = new byte[] { 0x02, 0x00, 0x00, 0x00, 0x8e, 0x00 }; stream.Write(array); array = new byte[] { 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00 }; stream.Write(array); array = new byte[] { 0x0a, 0x00, 0x00, 0x00, 0x24, 0x61, 0x73, 0x64, 0x66, 0x61, 0x73, 0x64, 0x66, 0x00 }; stream.Write(array); }
通过Wireshark抓包发现这些内容被合并成一个数据包,而非3个独立数据包:
char peer1_2[] = { /* Packet 9 */ 0x02, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x74, 0x61, 0x73, 0x64, 0x66, 0x61, 0x73, 0x64, 0x66, 0x00 };
我尝试在每次.Write()后调用.Flush(),但没有任何变化。请问我是否遗漏了什么?是否需要直接使用Sockets来实现该功能?我查阅了相关文档及NetworkStream的Write实现,但未找到解决方案。
问题根源
MSDN所说的“NetworkStream不缓冲”指的是它自身没有额外的用户态缓冲,但**TCP协议本身的Nagle算法(延迟确认+合并小数据包)**才是导致多次Write内容被合并的核心原因。操作系统的TCP栈会自动将短时间内的多个小发送请求合并成一个更大的数据包发送,以此提升网络传输效率。
解决方案
1. 禁用Nagle算法
直接操作底层Socket,设置NoDelay属性为true,这样TCP栈会立即发送每个数据包,不再等待合并:
// 若通过TcpClient获取NetworkStream,可这样获取并配置Socket TcpClient client = ...; // 你的TcpClient实例 client.Client.NoDelay = true; NetworkStream stream = client.GetStream();
2. 直接使用Socket发送
如果不想通过NetworkStream,直接调用Socket的Send方法,同样需要先设置NoDelay,效果一致:
Socket socket = ...; // 你的Socket实例 socket.NoDelay = true; socket.Send(new byte[] { 0x02, 0x00, 0x00, 0x00, 0x8e, 0x00 }); socket.Send(new byte[] { 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00 }); socket.Send(new byte[] { 0x0a, 0x00, 0x00, 0x00, 0x24, 0x61, 0x73, 0x64, 0x66, 0x61, 0x73, 0x64, 0x66, 0x00 });
注意事项
禁用Nagle算法会增加网络中的数据包数量,提升TCP头部的开销,牺牲一定的传输效率。因此仅在必须保证数据包独立性的场景下使用,比如自定义TCP协议要求严格按帧拆分的业务场景。
内容的提问来源于stack exchange,提问作者whackywhackamole

