c# - 来自 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
解决方案
由于数据包结构,您很可能会看到垃圾;TCP 只保证正确的字节将以正确的顺序到达(或流失败)——它没有说明它们到达的块。因此,您必须:
- 从 中捕获返回值
Read
,并且只处理来自任何块的那么多字节 - 执行您自己的框架 - 即将流分批成消息,与片段如何到达无关
如果您的消息总是固定大小,那么“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
,或者因异常而失败。
推荐阅读
- collision-detection - Godot collision not working when nodes added in specific order
- javascript - 在 auth0 中获取 accessToken
- python - pygame draw.rect() 无法绘制颜色
- c# - 如果我有内容 URI 而不是路径,是否可以在 Android 中使用 Xamarin.Essentials.Launcher 启动文件?
- oracle-sqldeveloper - 如何更改 Oracle SQL Developer 的默认 JDBC 驱动程序?
- laravel - Laravel 更新后删除旧图像
- python - Python/ Pandas:在 DataFrame 列中搜索一个单词并从字典中获取它的值
- java - 为什么将 ArrayList 传递给其他班级后它是空的?
- python - python sqlite3从excel(xls/xlsx)上传数据到数据库
- discord - discord selfbot 将相同的消息发送到不同服务器的不同通道