Проблемы при работе с сетью
От: XaSSeR  
Дата: 11.01.13 11:51
Оценка:
Есть програмка, которая ожидает данные из сети, а при получении выполняет определенные действия. Вот ее функции, осуществляющие чтение данных и их обработку:

        protected void RecieveMessage(object Stream)
        {
            try
            {
                NetworkStream netStream = Stream as NetworkStream;
                while (_client.Connected)
                {
                    byte[] buffer = new byte[4096];
                    int count = netStream.Read(buffer, 0, buffer.Length);
                    if (count != 0)
                    {
                        new Thread(ParseMessage).Start(NetMessage.Recive(buffer));
                        GetStringFromArrayBytes(buffer);
                        _log.WriteLog("От клиента " + _remotehost.Address.ToString() + " получены данные " + String.Join("; ", NetMessage.Recive(buffer)));//+". Число считанных байт - "+count.ToString()+". Данные - "+GetStringFromArrayBytes(buffer));
                    }
                    else _SendInfo(new string[] { "YouHere", "YouHere" });
                }
            }
            catch (System.IO.IOException)
            {
                _log.WriteLog("Клиент " + _remotehost.Address.ToString() + " отключен.");
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show("Ошибка чтения данных от сервера! " + ex.ToString());
            }
        }
        private void ParseMessage(object Message)
        {
            try
            {
                string[] message = Message as string[];
                //запрос на добавление в список рассылки
                switch (message[0])
                {
                    case "InsertMeToList": 
                        _delyvery = true;
                        SendMessage(new string[] { "YouInList", "YouInList" });
                        break;
                    case "YouHere":
                        SendMessage(new string[] { "ImHere", "ImHere" });
                        break;
                    case "ImHere":
                        break;
                    default: if (Command != null) Command(this, message); break;
                }
            }
            catch (Exception ex)
            {
                _log.WriteError("Ошибка при обработке команды клиента " + _remotehost.Address.ToString() + ". Сообщение: " + ex.Message);
            }
        }


Вот код используемого класса NetMessage


    /// <summary>
    /// Класс кодирования/декодирования сообщений для отправки по сети
    /// </summary>
    public static class NetMessage
    {
        /// <summary>
        /// Кодирует массив строк в массив байтов
        /// </summary>
        /// <param name="Strings">Кодируемый массив строк</param>
        /// <returns>Массив байт для отправки по сети</returns>
        public static byte[] ForSend(string[] Strings)
        {
            try
            {
                byte[] temp = new byte[4096];
                BitConverter.GetBytes(Strings.Length).CopyTo(temp, 0);
                int pos = 4;
                foreach (string str in Strings)
                    if (!String.IsNullOrEmpty(str))
                    {
                        int count = Encoding.UTF8.GetByteCount(str);
                        BitConverter.GetBytes(count).CopyTo(temp, pos);
                        pos += 4;
                        Encoding.UTF8.GetBytes(str, 0, str.Length, temp, pos);
                        pos += count;
                    }
                return temp;
            }
            catch
            {
                return new byte[] { };
            }
        }
        /// <summary>
        /// Определяет колличество строк во входном сообщении
        /// </summary>
        /// <param name="recieve">Принятый массив байт</param>
        /// <returns>Колличество строк в принятом сообщении</returns>
        public static int StringCount(byte[] recieve)
        {
            try
            {
                return BitConverter.ToInt32(recieve, 0);
            }
            catch
            {
                return 0;
            }
        }
        /// <summary>
        /// Декодирует массив байт
        /// </summary>
        /// <param name="recieve">Принятый массив байт</param>
        /// <returns>Массив строк сообщения</returns>
        public static string[] Recive(byte[] recieve)
        {
            try
            {
                string[] temp = new string[StringCount(recieve)];
                int pos = 0;
                for (int i = 0; i < StringCount(recieve); i++)
                {
                    int count = BitConverter.ToInt32(recieve, i * 4 + 4 + pos);
                    temp[i] = Encoding.UTF8.GetString(recieve, i * 4 + 8 + pos, count);
                    pos += count;
                }
                if (temp.Length == 0) return new string[] { "" };
                return temp;
            }
            catch
            {
                return new string[] { "" };
            }
        }
    }



Так, вот. Иногда при приеме данных от клиента из сети принимается пустая строка, то есть NetMessage.Recive(buffer) возвращает пустой массив, хотя должны быть данные. Почему такое происходит?
Re: Проблемы при работе с сетью
От: XaSSeR  
Дата: 11.01.13 12:19
Оценка:
Да, и еще кое что. Если добавить функцию

        string GetStringFromArrayBytes(byte[] buffer)
        {
            string temp = "";
            foreach (byte b in buffer)
                temp += Convert.ToInt32(b).ToString("D3") + "|";
            return temp;
        }


и вставить ее между этих двух строк (как это уже сделано в коде предыдущего сообщения)

                        new Thread(ParseMessage).Start(NetMessage.Recive(buffer));
                        _log.WriteLog("От клиента " + _remotehost.Address.ToString() + " получены данные " + String.Join("; ", NetMessage.Recive(buffer)));//+". Число считанных байт - "+count.ToString()+". Данные - "+GetStringFromArrayBytes(buffer));


то все работает нормально. Без нее работает через раз — то есть несколько нормально принятых сообщения, одно или два пустых (хотя данные там точно должны быть). После вставки вызома данной функции заработало. Объясните мне где я ошибся и как это убрать?
Re: Проблемы при работе с сетью
От: andrey82  
Дата: 11.01.13 12:34
Оценка:
Здравствуйте, XaSSeR, Вы писали:

XSS>Есть програмка, которая ожидает данные из сети, а при получении выполняет определенные действия. Вот ее функции, осуществляющие чтение данных и их обработку:


Для начала код стоит немного поправить...

1) NetworkStream.Read заполнит буфер не полностью, а только на реально прочитанные count байт. Это надо учитывать при обработке массива. Т.е. за 1 чтение данные из сети могут быть получены в общем случае не все.

XSS>
XSS>                    int count = netStream.Read(buffer, 0, buffer.Length);
XSS>


2) Зачем тут catch? Если только длина массива меньше 4 байт, но опять возвращаемся к п.1.
XSS>
XSS>        public static int StringCount(byte[] recieve)
XSS>

Аналогично с Recive() — строить тут логику управления на исключениях настоятельно не рекомендуется.


XSS>Так, вот. Иногда при приеме данных от клиента из сети принимается пустая строка, то есть NetMessage.Recive(buffer) возвращает пустой массив, хотя должны быть данные. Почему такое происходит?


А что при этом на входе у Recive() ?
Re[2]: Проблемы при работе с сетью
От: XaSSeR  
Дата: 11.01.13 13:20
Оценка:
Здравствуйте, andrey82, Вы писали:

A>1) NetworkStream.Read заполнит буфер не полностью, а только на реально прочитанные count байт. Это надо учитывать при обработке массива. Т.е. за 1 чтение данные из сети могут быть получены в общем случае не все.


Я в курсе. Но count мне не важно. Внутри буфера уже есть вся необходимая информация о длинне сообщения.

A>2) Зачем тут catch? Если только длина массива меньше 4 байт, но опять возвращаемся к п.1.


Остался с тех времен, когда я искал ошибку, и долго не мог найти.

A>Аналогично с Recive() — строить тут логику управления на исключениях настоятельно не рекомендуется.


А от этого может быть то, что у меня получается?

A>А что при этом на входе у Recive() ?


В представленном выше коде есть такая строка

_log.WriteLog("От клиента " + _remotehost.Address.ToString() + " получены данные " + String.Join("; ", NetMessage.Recive(buffer)));//+". Число считанных байт - "+count.ToString()+". Данные - "+GetStringFromArrayBytes(buffer));


Так вот, когда я раскоменчиваю строку, чтобы посмотреть что на выходе у Recive() (то есть что лежит в buffer) все начинает отлично работать и выводит что считано 4096 байт данных и все байты через |. А когда убираю данную строку, получаю иногда NetMessage.Recive(buffer) возвращает такое — new string[] { "" }, хотя должно быть new string[] { "Helo", "World" }. Поэтому я не знаю что на входе когда работает не правильно.
Re[3]: Проблемы при работе с сетью
От: andrey82  
Дата: 11.01.13 13:58
Оценка:
Здравствуйте, XaSSeR, Вы писали:

Вот это еще как то не очень:
if (count != 0)
new Thread(ParseMessage).Start(NetMessage.Recive(buffer));

netStream.Read прочитать может например первые 2 байта из потока, их обработка не пройдет, а при след. чтении — будут приняты остальные байты и на обработку пойдет фактически уже мусор.

XSS>Так вот, когда я раскоменчиваю строку, чтобы посмотреть что на выходе у Recive() (то есть что лежит в buffer) все начинает отлично работать и выводит что считано 4096 байт данных и все байты через |. А когда убираю данную строку, получаю иногда NetMessage.Recive(buffer) возвращает такое — new string[] { "" }, хотя должно быть new string[] { "Helo", "World" }. Поэтому я не знаю что на входе когда работает не правильно.


Так просто логгирование на входе в Recive() можно сделать...
Проблема похоже все таки в логике обработки читаемых из соединения байт.
Re[4]: Проблемы при работе с сетью
От: Аноним  
Дата: 11.01.13 14:30
Оценка:
Здравствуйте, andrey82, Вы писали:

A>Здравствуйте, XaSSeR, Вы писали:


A>Вот это еще как то не очень:

A>
if (count != 0)
A>new Thread(ParseMessage).Start(NetMessage.Recive(buffer));

A>netStream.Read прочитать может например первые 2 байта из потока, их обработка не пройдет, а при след. чтении — будут приняты остальные байты и на обработку пойдет фактически уже мусор.

XSS>>Так вот, когда я раскоменчиваю строку, чтобы посмотреть что на выходе у Recive() (то есть что лежит в buffer) все начинает отлично работать и выводит что считано 4096 байт данных и все байты через |. А когда убираю данную строку, получаю иногда NetMessage.Recive(buffer) возвращает такое — new string[] { "" }, хотя должно быть new string[] { "Helo", "World" }. Поэтому я не знаю что на входе когда работает не правильно.


A>Так просто логгирование на входе в Recive() можно сделать...

A>Проблема похоже все таки в логике обработки читаемых из соединения байт.

Почему же тогда после вставки функции вывода буфера все нормально заработало, если логика обработки хромала то ошибки были бы постоянно.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.