在不知道长度的情况下从 TcpClient.GetStream() 读取
Read from TcpClient.GetStream() without knowing the length
我正在研究基于 tcp 的通信协议。我所知
有很多方法可以确定何时结束阅读。
- 在消息结束时关闭连接
- 将消息的长度放在数据本身之前
- 使用分隔符;一些永远不会出现在普通数据中的值(或者总是会以某种方式被转义)
通常我会尝试通过 WiFi 网络发送文件(这可能不稳定且速度低)
- RSA和AES通信的原因我不喜欢每次都关闭连接(不能用1)
- 这是一个大文件,我无法预测它的长度,所以我无法行动
作为方法(不能使用 2)
- 读的时候检查特殊的东西,写的时候转义它需要很多过程(不能使用3)
- 此方法应兼容 c# 和 java。
你有什么建议?
更普遍的问题:
How to identify end of InputStream in java
C# - TcpClient - Detecting end of stream?
更多信息
我正在编写 TCP 客户端服务器通信
首先服务器生成并向客户端发送一个 RSA public 代码。
然后客户端生成AES(key,IV)并使用RSA加密回传
到这里一切都很好。
但是我想通过这个网络发送一个文件。这是我当前的数据包 EncryptUsingAES(new AES.IV(16 byte) +file.content(any size))
在服务器中我无法捕获客户端发送的所有数据。所以我需要知道要读取多少数据 (TcpClient.GetStream().read(buffer , 0 , buffersize) )
当前代码:
List<byte> message = new List<byte>();
int bytes = -1;
do
{
byte[] buffer = new byte[bufferrSize];
bytes = stream.Read(buffer, 0, bufferrSize);
if (bytes > 0)
{
byte[] tmp = new byte[bytes];
Array.Copy(buffer, tmp, bytes);
message.AddRange(tmp);
}
} while (bytes == bufferrSize);
最简单的方法是使用#2。如果您无法预测消息长度,请缓冲一定数量的字节(例如 1 KiB 或类似的东西),并为每个块插入一个长度 header,而不是为整个消息添加一次前缀。
你的第二种方法是最好的。在每个数据包前面加上数据包的长度将创建一个可靠的消息框架协议,如果正确完成,该协议将确保接收到所有数据,即使其大小与发送数据的大小相同(即,没有部分数据或数据混在一起)。
推荐的数据包结构:
[Data length (4 bytes)][Header (1 byte)][Data (?? bytes)]
- 所讨论的 header 是一个单字节,您可以使用它来指示 这是什么类型的 数据包,以便端点知道如何处理它.
正在发送文件
文件的发送者在 90% 的情况下 知道它要发送的数据量(毕竟,它通常将文件存储在本地) , 这意味着知道有多少文件已经发送没有问题。
我使用和推荐的方法是,您首先发送一个 "info packet",它向端点说明它将要接收一个文件以及该文件包含多少字节。之后您开始发送实际数据 - 最好是分块发送,因为一次处理整个文件效率低下(至少如果它是一个大文件)。
- 始终跟踪到目前为止您收到的文件的字节数。通过这样做,接收方可以自动判断何时收到了整个文件。
- 一次发送几千字节的文件(我使用 8192 字节 = 8 kB 作为文件缓冲区)。这样你就不必将整个文件读入内存,也不必同时加密它。
正在加密数据
处理加密不会有问题。如果您使用 length-prefixing,只需加密数据本身并保持数据长度 header 不变。数据长度 header 必须由加密数据的大小生成,如下所示:
- 加密数据。
- 获取加密数据的长度。
生成以下数据包:
[Encrypted data length][Encrypted data]
(如果需要,可在其中插入一个 header 字节)
正在接收加密文件
接收加密文件并知道何时收到所有内容实际上并不难。假设您使用上述方法发送文件,您只需:
- 接收加密数据包→解密。
- 获取解密数据的长度。
- 增加一个变量来跟踪收到的 file-bytes 数量。
- 如果收到的金额等于预期金额:关闭文件。
额外的resources/references
你可以参考我之前写的关于TCPlength-prefixed消息帧的两个回答:
我正在研究基于 tcp 的通信协议。我所知 有很多方法可以确定何时结束阅读。
- 在消息结束时关闭连接
- 将消息的长度放在数据本身之前
- 使用分隔符;一些永远不会出现在普通数据中的值(或者总是会以某种方式被转义)
通常我会尝试通过 WiFi 网络发送文件(这可能不稳定且速度低)
- RSA和AES通信的原因我不喜欢每次都关闭连接(不能用1)
- 这是一个大文件,我无法预测它的长度,所以我无法行动 作为方法(不能使用 2)
- 读的时候检查特殊的东西,写的时候转义它需要很多过程(不能使用3)
- 此方法应兼容 c# 和 java。
你有什么建议?
更普遍的问题:
How to identify end of InputStream in java
C# - TcpClient - Detecting end of stream?
更多信息
我正在编写 TCP 客户端服务器通信
首先服务器生成并向客户端发送一个 RSA public 代码。
然后客户端生成AES(key,IV)并使用RSA加密回传
到这里一切都很好。
但是我想通过这个网络发送一个文件。这是我当前的数据包 EncryptUsingAES(new AES.IV(16 byte) +file.content(any size))
在服务器中我无法捕获客户端发送的所有数据。所以我需要知道要读取多少数据 (TcpClient.GetStream().read(buffer , 0 , buffersize) ) 当前代码:
List<byte> message = new List<byte>();
int bytes = -1;
do
{
byte[] buffer = new byte[bufferrSize];
bytes = stream.Read(buffer, 0, bufferrSize);
if (bytes > 0)
{
byte[] tmp = new byte[bytes];
Array.Copy(buffer, tmp, bytes);
message.AddRange(tmp);
}
} while (bytes == bufferrSize);
最简单的方法是使用#2。如果您无法预测消息长度,请缓冲一定数量的字节(例如 1 KiB 或类似的东西),并为每个块插入一个长度 header,而不是为整个消息添加一次前缀。
你的第二种方法是最好的。在每个数据包前面加上数据包的长度将创建一个可靠的消息框架协议,如果正确完成,该协议将确保接收到所有数据,即使其大小与发送数据的大小相同(即,没有部分数据或数据混在一起)。
推荐的数据包结构:
[Data length (4 bytes)][Header (1 byte)][Data (?? bytes)]
- 所讨论的 header 是一个单字节,您可以使用它来指示 这是什么类型的 数据包,以便端点知道如何处理它.
正在发送文件
文件的发送者在 90% 的情况下 知道它要发送的数据量(毕竟,它通常将文件存储在本地) , 这意味着知道有多少文件已经发送没有问题。
我使用和推荐的方法是,您首先发送一个 "info packet",它向端点说明它将要接收一个文件以及该文件包含多少字节。之后您开始发送实际数据 - 最好是分块发送,因为一次处理整个文件效率低下(至少如果它是一个大文件)。
- 始终跟踪到目前为止您收到的文件的字节数。通过这样做,接收方可以自动判断何时收到了整个文件。
- 一次发送几千字节的文件(我使用 8192 字节 = 8 kB 作为文件缓冲区)。这样你就不必将整个文件读入内存,也不必同时加密它。
正在加密数据
处理加密不会有问题。如果您使用 length-prefixing,只需加密数据本身并保持数据长度 header 不变。数据长度 header 必须由加密数据的大小生成,如下所示:
- 加密数据。
- 获取加密数据的长度。
生成以下数据包:
[Encrypted data length][Encrypted data]
(如果需要,可在其中插入一个 header 字节)
正在接收加密文件
接收加密文件并知道何时收到所有内容实际上并不难。假设您使用上述方法发送文件,您只需:
- 接收加密数据包→解密。
- 获取解密数据的长度。
- 增加一个变量来跟踪收到的 file-bytes 数量。
- 如果收到的金额等于预期金额:关闭文件。
额外的resources/references
你可以参考我之前写的关于TCPlength-prefixed消息帧的两个回答: