通过 NetworkStream 传输项目会导致某些数据被破坏

发布于 2024-09-24 05:11:30 字数 3205 浏览 1 评论 0原文

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

示例:我执行 .Read 来获取文件名,我将获取文件名、文件大小和文件原始数据的数据。我认为发生这种情况是因为服务器只是执行 .Write 并将数据写入流,而第一个 .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);

        }

    }

I am sending a filename(string), filesize(int), and the file(byte[]). What is happening is that in certain cases, depending on how quickly the data gets processed on the server side, the NetworkStream has read the data that I dont need yet.

Example: I do a .Read to get the filename, I will get the data for filename,filesize and the files raw data. I assume this happens because the server just does a .Write and writes the data to the stream when the first .Read hasnt executed yet. This ends up clobbering my filesize .Read. Now when I do a .Read for my filesize I am showing a HUGE number and when I go to read for the file itself and allocation a new byte[] based on the read files size i get an OutOfMemory exception.

How do I synchronize the Read properly? The examples I am finding on the net do it the way I do.

Some code:

   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;
    }

I have tried using a offset...with no luch. Your help is appreciated.

I started another question but it relates to something else.

Thanks in advance
Sean

EDIT: Here is my send string code:

  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);

        }

    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

固执像三岁 2024-10-01 05:11:30

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

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

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

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

Read() the way you're calling it will pull 256 bytes; that's what stringFromClient.Length was set up to be.

You have two options for accurately chopping up a stream of data, both of which involve knowing or creating some way of figuring out the border between data. They are delimited-stream, and fixed-length stream.

For a delimited format, you choose a character that you will not use as a valid part of the filename or size (a pipe, a space, a tab character, newline, etc) and insert one in between the filename and size, and between size and file contents. Then, read one byte at a time into the array until you hit a delimiter. The bytes you've read so far except the delimiter are your data; extract into a useable form and return. This generally makes streams shorter, but requires one possible character to never be used.

For a fixed-size format, determine a number of bytes that any reasonable value for the data would not exceed. Filenames, for instance, cannot be more than 256 chars (and more likely no more than about 50; some older/simpler OSes still limit to 8). File sizes cannot be more than 2^64 bytes in any Windows environment, and that number can be expressed in 4 bytes of raw numeric data, or 20 characters of a string. So, whatever limits you choose, pad the data using an appropriate buffer; for raw numbers, cast to an Int64 and chop it into bytes, while for strings, pad with spaces. Then, you know the first X bytes exactly will be the filename, the next Y bytes exactly will be the file size, and anything after that is content. This makes the stream larger, but the contents can be anything as there are no special or reserved byte values.

记忆之渊 2024-10-01 05:11:30

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

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

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.

楠木可依 2024-10-01 05:11:30

您不能依赖一次从流中读取的字节数,您的 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)

you can not rely on how many bytes are read from a stream at a time, there's a bug in your ReadStringFromServer method, assuming the string is of a fixed length (256).

instead of:

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

try:

 do
 {
    i = currentStream.Read(stringFromClient, 0, 256 - builtString.Length);
    builtString+=(System.Text.Encoding.ASCII.GetString(stringFromClient, 0, i));
 } while(builtString.Length < 256)
放低过去 2024-10-01 05:11:30

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

A better solution would probably be to serialize all your data (into JSON for example) and pass everything over the network stream.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文