【问题标题】:Transfering items over NetworkStream causes some data to get clobbered通过 NetworkStream 传输项目会导致一些数据被破坏
【发布时间】:2010-09-20 21:01:51
【问题描述】:

我正在发送一个文件名(字符串)、文件大小(int)和文件(字节[])。正在发生的事情是,在某些情况下,根据服务器端处理数据的速度,NetworkStream 已经读取了我还不需要的数据。

示例:我执行 .Read 来获取文件名,我将获取文件名、文件大小和文件原始数据的数据。我认为发生这种情况是因为服务器只是执行 .Write 并将数据写入流中,而第一个 .Read 尚未执行。这最终破坏了我的文件大小.Read。现在,当我为我的文件大小执行 .Read 时,我显示了一个巨大的数字,当我去读取文件本身并根据读取的文件大小分配一个新的 byte[] 时,我得到了 OutOfMemory 异常。

如何正确同步读取?我在网上找到的例子都是按照我的方式做的。

一些代码:

   private void ReadandSaveFileFromServer(TcpClient clientATF, NetworkStream currentStream, string locationToSave)
    {
        int fileSize = 0;
        string fileName = "";
        int readPos = 0;
        int bytesRead = -1;

        fileName = ReadStringFromServer(clientATF, currentStream);

        fileSize = ReadIntFromServer(clientATF, currentStream);


        byte[] fileSent = new byte[fileSize];

        while (bytesRead != 0)
        {
            if (currentStream.CanRead && clientATF.Connected)
            {

                bytesRead = currentStream.Read(fileSent, readPos, fileSent.Length);

                readPos += bytesRead;
                if (readPos == bytesRead)
                {
                    break;
                 }

            }
            else
            {
                WriteToConsole("Log Transfer Failed");
                break;
            }
        }
        WriteToConsole("Log Recieved");

        File.WriteAllBytes(locationToSave + "\\" + fileName, fileSent);


    }


 private string ReadStringFromServer(TcpClient clientATF, NetworkStream currentStream)
    {
        int i = -1;
        string builtString = "";
        byte[] stringFromClient = new byte[256];



            if (clientATF.Connected && currentStream.CanRead)
            {

                i = currentStream.Read(stringFromClient, 0, stringFromClient.Length);
                builtString = System.Text.Encoding.ASCII.GetString(stringFromClient, 0, i);

            }

            else
            {
                return "Connection Error";
            }



        return builtString;

    }

    private int ReadIntFromServer(TcpClient clientATF, NetworkStream currentStream)
    {
        int i = -1 ;
        int builtInteger = 0;
        byte[] integerFromClient = new byte[256];
        int offset = 0;


            if (clientATF.Connected && currentStream.CanRead)
            {

                i = currentStream.Read(integerFromClient, offset, integerFromClient.Length);

                builtInteger = BitConverter.ToInt32(integerFromClient, 0);

            }

            else
            {
                return -1;
            }



        return builtInteger;
    }

我尝试过使用偏移量...没有运气。感谢您的帮助。

我提出了另一个问题,但它与其他问题有关。

提前致谢 肖恩

编辑:这是我的发送字符串代码:

  private void SendToClient( TcpClient clientATF,  NetworkStream currentStream, string messageToSend)
    {
        byte[] messageAsByteArray = new byte[256];

        messageAsByteArray = Encoding.ASCII.GetBytes(messageToSend);

        if (clientATF.Connected && currentStream.CanWrite)
        {
            //send the string to the client

                currentStream.Write(messageAsByteArray, 0, messageAsByteArray.Length);

        }

    }

【问题讨论】:

    标签: c# .net tcpclient networkstream


    【解决方案1】:

    Read() 您调用它的方式将提取 256 个字节;这就是 stringFromClient.Length 的设置。

    您有两种方法可以准确地分割数据流,这两种方法都涉及了解或创建某种方法来确定数据之间的边界。它们是定界流和定长流。

    对于分隔格式,您选择一个不会用作文件名或大小的有效部分的字符(竖线、空格、制表符、换行符等)并在文件名和大小之间插入一个, 以及大小和文件内容之间。然后,一次将一个字节读入数组,直到遇到分隔符。到目前为止,除了分隔符之外,您已读取的字节是您的数据;提取成可用的形式并返回。这通常会使流更短,但需要一个可能的字符从不使用。

    对于固定大小的格式,确定任何合理的数据值都不会超过的字节数。例如,文件名不能超过 256 个字符(更有可能不超过 50 个;一些较旧/较简单的操作系统仍限制为 8 个)。在任何 Windows 环境中,文件大小不能超过 2^64 字节,并且该数字可以用 4 字节的原始数字数据或 20 个字符的字符串来表示。因此,无论您选择什么限制,都使用适当的缓冲区填充数据;对于原始数字,转换为 Int64 并将其切成字节,而对于字符串,用空格填充。然后,您知道前 X 字节将是文件名,接下来的 Y 字节将是文件大小,之后的任何内容都是内容。这使得流更大,但内容可以是任何内容,因为没有特殊或保留的字节值。

    【讨论】:

    • @Sean:你可能想在这里用 ascii 0 填充。
    • 您可以使用数字 32(空格字符的 ASCII 值)。零字节值“更好”,因为它更明显是填充,但是一些文件框架将零代码读取为 EOF,这可能会干扰文件传输的基本工作。
    • 零通常是字符串的结尾,就像在 C/C++ 中一样。传统的 EOF 是 ASCII 26,Ctrl+Z。我对在这里使用空格的担忧是它们在文件名中是合法的。
    【解决方案2】:

    TCP/IP 是流式传输,而不是数据报,因此您想要的行为不存在。你可以通过让流包含足够的信息来解决它。

    换句话说,您可以在一行文本之后使用分隔符,例如 CR/LF,或者您可以给出即将到来的数据的长度。您还可以在适当的情况下使用固定大小的字段。

    【讨论】:

      【解决方案3】:

      您不能依赖一次从流中读取多少字节,您的 ReadStringFromServer 方法中有一个错误,假设字符串的长度是固定的 (256)。

      代替:

       i = currentStream.Read(stringFromClient, 0, stringFromClient.Length);
       builtString = System.Text.Encoding.ASCII.GetString(stringFromClient, 0, i);
      

      试试:

       do
       {
          i = currentStream.Read(stringFromClient, 0, 256 - builtString.Length);
          builtString+=(System.Text.Encoding.ASCII.GetString(stringFromClient, 0, i));
       } while(builtString.Length < 256)
      

      【讨论】:

      • @Sean:这是不正确的。首先,当字符串变成超过 256 个字节时,它会静默失败。其次,您分配了一个缓冲区,然后将其丢弃。第三,你没有以任何方式传递长度,所以没有办法让读者确定它。
      • @Sean:我说的是SendToClient。你初始化messageAsByteArray,然后立即覆盖它。
      【解决方案4】:

      更好的解决方案可能是将所有数据序列化(例如转换为 JSON)并通过网络流传递所有数据。

      【讨论】:

      • 这不是一个糟糕的主意,尽管它可能有点矫枉过正。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-14
      • 1970-01-01
      • 2017-10-29
      • 1970-01-01
      • 1970-01-01
      • 2012-03-24
      • 1970-01-01
      相关资源
      最近更新 更多