首页 > 解决方案 > 来自 NetworkStream 的 Write() 和 Read() 原始字节,数据在某些字节处有所不同

问题描述

我已经编写了一些代码,用于在发送之前使用具有已知大小的 NetworkStream 发送字节 [] 数组,但是发送的数据和接收的数据在某些位置是不同的。

MAXSIZE 是我要发送的数据的已知大小。

    public static void SendBytes(TcpClient clientSocket, byte[] outStream)
    {
        Debug.WriteLine("SendBytes() number of bytes: " + outStream.Length.ToString());

        NetworkStream serverStream = clientSocket.GetStream();

        serverStream.Write(outStream, 0, outStream.Length);
        //serverStream.Flush();
    }

    public static byte[] ReceiveBytes(TcpClient clientSocket, int MAX_SIZE)
    {
        Debug.WriteLine("[" + DateTime.Now.ToString("G") + "] - " + "ReceiveBytes() started.");

        NetworkStream networkStream = clientSocket.GetStream();

        byte[] bytesFrom = new byte[MAX_SIZE];
        clientSocket.ReceiveBufferSize = MAX_SIZE;

        networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);

        Debug.WriteLine("[" + DateTime.Now.ToString("G") + "] - " + "ReceiveBytes(), received number of raw bytes: " + bytesFrom.Length.ToString());

        return CommonUtils.SubArray(bytesFrom, 0, MAX_SIZE);
    }

如果发送数据(十六进制字节): a7 fc d0 51 0e 99 cf 0d 00 ,接收到的数据是: a7 fc d0 51 0e 99 cf 0d 53

标签: c#arraysnetworkstream

解决方案


由于数据包结构,您很可能会看到垃圾;TCP 只保证正确的字节将以正确的顺序到达(或流失败)——它没有说明它们到达的块。因此,必须:

  1. 从 中捕获返回值Read,并且只处理来自任何块的那么多字节
  2. 执行您自己的框架 - 即将流分批成消息,与片段如何到达无关

如果您的消息总是固定大小,那么“2”将变为“缓冲数据,直到我有至少 N 个字节,然后以 N 块处理数据,保留剩余的任何内容,然后恢复缓冲”。但在一般情况下,它可能是“缓冲直到我看到一个标记值,例如换行”,或者“缓冲直到我有一个完整的标头,然后解析标头以查看预期有多少数据,然后缓冲直到我有那么多数据”。

有一些工具和实用程序可以帮助简化去帧和处理积压工作 - 例如,使用新的“管道”API,它只是检查管道并告诉管道你想要消耗多少(而不是它给你一切,你现在无法拒绝数据)——但从Stream“管道”切换到大多数人来说是相当多的变化。

在您的情况下,您可能可以使用:

byte[] bytesFrom = new byte[MAX_SIZE];
int outstanding = MAX_SIZE, read, offset = 0;
while (outstanding > 0 && (read = networkStream.Read(bytesFrom, offset, outstanding)) > 0)
{
    offset += read;
    outstanding -= read;
}
if (outstanding != 0) throw new EndOfStreamException();

这将创建一个完全填充的读取循环bytesFrom,或者因异常而失败。


推荐阅读