You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NetworkStream Read()过慢问题:7.5MB文件耗时约9分钟

问题分析与解决方案

首先,你的传输速度慢的核心问题出在接收端的逻辑错误,而不是缓冲区大小本身。不管是最初的一次性读取还是后来的循环读取,都没有正确处理TCP流的特性——TCP是字节流,NetworkStream.Read()方法不会保证一次性读取你请求的所有字节,它返回的是实际读取到的字节数,而你的代码完全忽略了这个返回值,导致数据丢失、重复读取无效内容,最终拖慢了整个传输过程。

你的接收代码存在的关键问题:

  • 忽略Read()的返回值:你一直假设每次都能读满整个缓冲区,但实际网络传输中,每次读取的字节数是不确定的,忽略返回值会导致你无法跟踪实际接收的数据量。
  • 没有累加接收的数据:第二个版本的循环中,每次读取都会覆盖tmp_bytesFrom,之前接收的内容全部丢失,永远凑不齐完整的文件数据+结束标记。
  • 结束标记检测逻辑错误:只检测结束标记的第一个字节,且没有在已接收的完整数据中查找完整的结束序列,可能导致提前终止循环或者永远无法找到标记。

修正后的接收代码

下面是修复后的完整接收逻辑,解决了上述所有问题:

private static int CHUNK_SIZE = 4096; // 使用合理的缓冲区大小,4KB是常见的最优值
public static byte[] ReceiveBytes(TcpClient clientSocket)
{
    Debug.WriteLine($"[{DateTime.Now:G}] - ReceiveBytes() started.");
    clientSocket.NoDelay = true;
    NetworkStream networkStream = clientSocket.GetStream();
    var receivedData = new List<byte>(); // 用List来动态累加接收的数据
    byte[] buffer = new byte[CHUNK_SIZE];
    int bytesRead;

    // 循环读取数据,直到找到完整的结束标记
    while (true)
    {
        bytesRead = networkStream.Read(buffer, 0, buffer.Length);
        if (bytesRead == 0)
        {
            // 连接已关闭,没有更多数据
            throw new IOException("Connection closed before receiving complete data.");
        }

        // 将本次读取的有效数据添加到累加列表中
        receivedData.AddRange(buffer.Take(bytesRead));

        // 检查已接收的数据中是否包含完整的结束标记
        int endMarkerIndex = FindEndMarker(receivedData, ProgramState.end_networkdata_bytes);
        if (endMarkerIndex != -1)
        {
            // 截取结束标记之前的数据
            byte[] rawData = receivedData.Take(endMarkerIndex).ToArray();
            // 解码后返回
            byte[] decodedData = EncoderDecoder.Decoder(rawData);
            Debug.WriteLine($"[{DateTime.Now:G}] - ReceiveBytes() completed. Received raw bytes: {rawData.Length}, decoded bytes: {decodedData.Length}");
            return decodedData;
        }
    }
}

// 辅助方法:在字节列表中查找完整的结束标记序列
private static int FindEndMarker(List<byte> data, byte[] endMarker)
{
    if (data.Count < endMarker.Length)
        return -1;

    // 遍历所有可能的起始位置,检查是否匹配完整的结束标记
    for (int i = 0; i <= data.Count - endMarker.Length; i++)
    {
        bool match = true;
        for (int j = 0; j < endMarker.Length; j++)
        {
            if (data[i + j] != endMarker[j])
            {
                match = false;
                break;
            }
        }
        if (match)
        {
            return i;
        }
    }
    return -1;
}

关键改进点说明:

  1. 跟踪实际读取的字节数:每次调用networkStream.Read()后,用返回的bytesRead来确定本次读取的有效数据长度,避免处理缓冲区中未填充的无效字节。
  2. 动态累加数据:使用List<byte>来逐步累加所有接收的数据,不会丢失之前读取的内容。
  3. 正确检测结束标记:通过辅助方法FindEndMarker在已接收的完整数据中查找完整的结束标记序列,而不是单个字节,同时考虑了标记跨缓冲区的情况。
  4. 合理的缓冲区大小:使用4KB(4096字节)的缓冲区,这是网络IO中经过优化的常见大小,过大或过小的缓冲区都会影响效率。

额外建议

  • 发送端可以保持现有逻辑,但建议同样检查serverStream.Write()的返回值,确保所有字节都被发送(虽然TCP会保证可靠传输,但检查返回值是良好的编程习惯)。
  • 如果传输大文件,还可以考虑在发送前先发送文件的长度信息,这样接收端可以知道需要接收多少数据,不需要依赖结束标记,进一步提升可靠性和效率。

内容的提问来源于stack exchange,提问作者Bủi Văn Thủ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:23:26