从TcpClient.GetStream()读取而不知道长度

时间:2016-09-04 21:46:45

标签: java c# tcp stream tcpclient

我正在研究tcp基础通信协议。我所知 有很多方法可以确定何时结束阅读。

  1. 关闭邮件末尾的连接
  2. 将消息的长度放在数据本身之前
  3. 使用分隔符;一些永远不会出现在正常数据中的值(或者总会以某种方式转义)
  4. 通常我试图通过WiFi网络发送文件(可能是不稳定和低速)

    • RSA和AES通信的原因我不想每次都关闭连接(不能使用1
    • 这是一个大文件,我无法预测它的长度,所以我不能采取行动 作为方法(不能使用2
    • 在阅读时检查特殊内容并在编写时将其转义需要大量处理(不能使用3
    • 此方法应与c#和java兼容。

    你的建议是什么?

    更一般的问题:

    How to identify end of InputStream in java

    C# - TcpClient - Detecting end of stream?

    更多信息

    我正在编写TCP客户端服务器通信

    首先,服务器生成并向客户端发送RSA公共代码。

    然后客户端将生成AES(密钥,IV)并使用RSA加密将其发回。

    直到这里一切都很好。

    但我想通过这个网络发送文件。这是我当前的数据包EncryptUsingAES(新的AES.IV(16字节)+ file.content(任何大小))

    在服务器中,我无法捕获客户端发送的所有数据。所以我需要知道要读取多少数据(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 个答案:

答案 0 :(得分:1)

你的第二种方法是最好的方法。使用数据包的长度为每个数据包添加前缀将创建一个可靠的消息成帧协议,如果正确完成,即使与您发送的数据相同,也可以确保收到所有数据(即,没有部分数据或数据存在集中在一起)。

  • 推荐的数据包结构:

    [Data length (4 bytes)][Header (1 byte)][Data (?? bytes)]
    

    - 有问题的标题是一个单字节,您可以用它来指示这是什么类型的数据包,以便端点知道如何处理它。


发送文件

文件的发件人在90%的情况下知道它将要发送的数据量(毕竟,它通常将文件存储在本地),这意味着知道已经发送了多少文件就没问题了。

我使用和推荐的方法是首先发送一个&#34; info数据包&#34;,它向端点解释它即将接收文件以及该文件包含多少字节。之后,您开始发送实际数据 - 最优选的是块,因为一次性处理整个文件效率很低(至少如果它是一个大文件)。

  • 始终跟踪到目前为止您收到的文件的字节数。通过这样做,接收器可以自动判断它何时收到整个文件。
  • 一次发送几千字节的文件(我使用8192字节= 8 kB作为文件缓冲区)。这样你就不必将整个文件读入内存,也不必同时加密所有文件。


加密数据

处理加密不会有问题。如果使用长度前缀,则只加密数据本身并保持数据长度标头不变。然后必须根据加密数据的大小生成数据长度标题,如下所示:

  1. 加密数据。
  2. 获取加密数据的长度。
  3. 生成以下数据包:

    [Encrypted data length][Encrypted data]
    

    (如果需要,在那里插入标题字节)


  4. 接收加密文件

    接收加密文件并知道何时收到所有内容实际上并不是很难。假设您使用上述方法发送文件,您只需:

    1. 接收加密数据包→解密。
    2. 获取解密数据的长度。
    3. 增加一个变量,跟踪收到的文件字节数。
    4. 如果收到的金额等于预期金额:关闭文件。

    5. 其他资源/参考资料

      你可以参考我之前写的关于TCP长度前缀消息框架的两个答案:

答案 1 :(得分:0)

最简单的方法是使用你的#2。如果你无法预测消息长度,请缓冲一定数量的字节(如1 KiB或这些行中的某些内容),并为每个块插入一个长度标头,而不是为整个消息添加一次。