0

我正在发送一个文件名(字符串)、文件大小(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);

        }

    }
4

4 回答 4

1

TCP/IP is streaming, not datagrams, so the behavior you want is just not there. You can work around it by having the stream contain enough information to be parsed out.

In other words, you could use delimiters, such as a CR/LF after a line of text, or you could give the length of an upcoming piece of data. You can also use fixed-size fields, where appropriate.

于 2010-09-20T21:08:15.047 回答
1

您不能依赖一次从流中读取多少字节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)
于 2010-09-20T21:19:23.020 回答
1

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

于 2010-09-20T21:22:14.933 回答
1

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

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

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

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

于 2010-09-20T21:24:42.773 回答