Передача элементов через NetworkStream приводит к засорению некоторых данных - PullRequest
0 голосов
/ 21 сентября 2010

Я отправляю имя файла (строка), размер файла (int) и файл (байт []).Происходит следующее: в некоторых случаях, в зависимости от того, как быстро обрабатываются данные на стороне сервера, NetworkStream считывает данные, которые мне пока не нужны.

Пример: я делаю .Read, чтобы получить имя файла, я получу данные для имени файла, размера файла и файлов необработанных данных.Я предполагаю, что это происходит потому, что сервер просто выполняет .Write и записывает данные в поток, когда первый .Read еще не выполнен.Это заканчивает тем, что ударило мой размер файла .Читайте.Теперь, когда я делаю .Read для моего размера файла, я показываю ОГРОМНОЕ число, и когда я иду для чтения самого файла и выделяю новый байт [] на основе размера прочитанных файлов, я получаю исключение 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 ]

1 голос
/ 21 сентября 2010

Read () способ, которым вы звоните, вытащит 256 байт; вот что было установлено в stringFromClient.Length.

У вас есть два варианта для точного разделения потока данных, каждый из которых включает в себя знание или создание какого-либо способа определения границы между данными. Это поток с разделителями и поток с фиксированной длиной.

Для формата с разделителями вы выбираете символ, который вы не будете использовать в качестве допустимой части имени файла или размера (пробел, пробел, символ табуляции, символ новой строки и т. Д.), И вставляете его между именем файла и размером и между размером и содержимым файла. Затем считывайте по одному байту за раз в массив, пока не попадете в разделитель. Байты, которые вы прочитали до сих пор, кроме разделителя, являются вашими данными; извлечь в пригодную для использования форму и вернуть. Как правило, это делает потоки короче, но требует, чтобы один возможный символ никогда не использовался.

Для формата с фиксированным размером определите количество байтов, которое любое разумное значение для данных не должно превышать. Например, имена файлов не могут содержать более 256 символов (и, скорее всего, не более 50; некоторые старые / более простые ОС по-прежнему ограничиваются 8). Размеры файлов не могут превышать 2 ^ 64 байта в любой среде Windows, и это число может быть выражено в 4 байтах необработанных числовых данных или в 20 символах строки. Итак, какие бы ограничения вы ни выбрали, добавьте данные в соответствующий буфер; для необработанных чисел приведите к Int64 и разделите его на байты, а для строк запишите пробелы. Затем вы знаете, что первые X байтов точно будут именами файлов, следующие Y байтов точно будут размерами файлов, и все, что после этого будет содержимым. Это увеличивает поток, но его содержимое может быть любым, поскольку нет специальных или зарезервированных значений байтов.

1 голос
/ 21 сентября 2010

вы не можете полагаться на то, сколько байтов читается из потока за раз, в вашем методе 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)
1 голос
/ 21 сентября 2010

Лучшим решением, вероятно, будет сериализация всех ваших данных (например, в JSON) и передача всего через сетевой поток.

1 голос
/ 21 сентября 2010

TCP / IP потоковый, а не дейтаграммы, поэтому нужного вам поведения просто нет.Вы можете обойти это, имея поток, содержащий достаточно информации для анализа.

Другими словами, вы можете использовать разделители, такие как CR / LF после строки текста, или вы можете указать длинупредстоящей части данных.При необходимости вы также можете использовать поля фиксированного размера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...