Модераторы: Snowy, Poseidon, MetalFan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Сокеты и цикл с отсылкой packed record, ТСР сервер и клиент, сокеты, цикл и т.д. 
:(
    Опции темы
StealeR
Дата 25.9.2014, 17:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 37
Регистрация: 14.11.2005

Репутация: нет
Всего: нет



Всем привет. Прошу помощи...Проблема следующая. Есть серверная часть, которая собирает некоторую инфу в динамический массив, который потом обрабатывается. С этим все в норме. 

Есть клиентская часть (ClientSocket1: TClientSocket;), которая отлично подключается и периодически отсылает таймером на сервер строку-команду, указывающую серверу, что ему отправить в ответ:
Код

    if ClientSocket1.Socket.Connected then
    ClientSocket1.Socket.SendText('all');


Сервер (ServerSocket1: TServerSocket;), при приеме этой команды делает следующее:
Код

// объявляем тип данных для отсылки и приема
type TRes2Draw = packed record
    Count       : Byte;
    Current     : Byte;
    Options     : String[250];
    Result      : String[20];
    Host        : String[50];
    Task        : Byte;
end;
// бла-бла-бла, некоторый несетевой код, который работает и отлажен

// а вот тут работаем с сетью
procedure TUgObServer.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var cmd     : AnsiString;
    i       : Byte;
    Item    : TRes2Draw;
begin
    cmd:=Socket.ReceiveText;
    if cmd='all' then
    begin
        if Length(Arr)>0 then
        begin
            try
            // формируем результаты
            Item.Count:=Length(Arr);
            for i:=0 to Length(Arr)-1 do
            begin
                Item.Current:=i;
                Item.Options := Arr[i].Options;
                Item.Result := Arr[i].Resultat;
                Item.Task := Arr[i].Task;
                Item.Host := Arr[i].Host;
                // отсылаем
                Socket.SendBuf(Item, SizeOf(Item));
            end;
            finally

            end;
        end;
    end;
end;

т.е. при получении команды "отдать всю инфу" запускаем цикл по массиву, на каждой итерации заполняем переменную новыми данными и отсылаем. Элементов в массиве (и соответственно, итераций) немного, от нуля до 30-40 максимум, зависит от конфигурации сервера, и в течении сеанса связи неизменно. Опрос происходит примерно раз в секунду.

Затык происходит на клиенте. При событии получения OnRead смотрю сколько байт получено и вижу, что размер полученных данных варьируется и всегда кратен размеру одной записи. Честно говоря, расчитывал, что будет проще - отправил одну запись и получил тоже одну, а не пачку из 1-20 штук. Код на приёмке следующий:
Код

procedure TForm1.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
var
    cnt: Byte;
    r1, r2: Integer;
    Item:TRes2Draw;

begin
    try
        if Socket.ReceiveLength>=SizeOf(Item) then
        begin
            r1:=Socket.ReceiveBuf(Item, SizeOf(Item));
            if r1=SizeOf(Item) then
            begin
                if Length(ResMas)<>Item.Count then SetLength(ResMas, Item.Count);
                ResMas[Item.Current]:=Item;
            end;
        end;
    finally

    end;
end;

Из-за того, что принимаемые данные содержат заранее неизвестное N-ое кол-во записей, а принимаем только одну (первую), то и имеем на выходе наполовину заполненный массив (который отдельным потоком отрисовывает на форме эту инфу).

Кто с подобными вещами уже сталкивался, подскажите, как быть. Может быть есть какой обходной путь? Или наведите на мысль верную, буду премного благодарен, а то поскольку по профессии не являюсь программистом, то собственного тяму не хватает разобраться в ситуёвине за отсутствием опыта...

Это сообщение отредактировал(а) StealeR - 25.9.2014, 17:30
PM MAIL   Вверх
kami
Дата 25.9.2014, 22:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 6
Всего: 72



Цитата(StealeR @  25.9.2014,  17:21 Найти цитируемый пост)
 ClientSocket1.Socket.SendText('all');
 отправлять строку (скорее всего - переменной длины) в TCP-соединение - это чревато.

Цитата(StealeR @  25.9.2014,  17:21 Найти цитируемый пост)
 размер полученных данных варьируется

Правильно, так и должно быть

Цитата(StealeR @  25.9.2014,  17:21 Найти цитируемый пост)
всегда кратен размеру одной записи. 

Неправильно, это частный случай. На самом деле - может и не быть кратен. Кратность получилась скорее всего из-за работы в пределах одного компьютера, либо в ненагруженной локалке.

Цитата(StealeR @  25.9.2014,  17:21 Найти цитируемый пост)
Кто с подобными вещами уже сталкивался, подскажите, как быть.

В очередной раз протолкну свои велосипеды smile
При их использовании - что отправишь, то и получишь. Безо всяких кратностей, всегда 1:1

Это сообщение отредактировал(а) kami - 25.9.2014, 22:20

Присоединённый файл ( Кол-во скачиваний: 16 )
Присоединённый файл  SimpleDataTransfer.zip 14,54 Kb
PM MAIL WWW   Вверх
StealeR
Дата 26.9.2014, 06:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 37
Регистрация: 14.11.2005

Репутация: нет
Всего: нет



Алексей, благодарю за предложенную альтернативу, буду пробовать smile

По поводу кратности: да, действительно, все это тестировалось в не сильно нагруженной сети и по результатам логирования длины принимаемых данных (несколько сотен отправок/приемов) результат в 100% случаев оказывался кратен
PM MAIL   Вверх
drkot
Дата 27.9.2014, 00:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ищущий
***


Профиль
Группа: Завсегдатай
Сообщений: 1042
Регистрация: 5.5.2006

Репутация: 0
Всего: 8



Цитата(StealeR @  25.9.2014,  18:21 Найти цитируемый пост)
заранее неизвестное N-ое кол-во записей

что мешает передавать количество?

Также ничего не мешает запрашивать записи по номерам, то 0 и выше... когда доходим до конца списка сервер возвращает признак ошибки

Добавлено через 2 минуты и 43 секунды
как вариант протокола:
1) количество записей
2) длинна записи
3) тело записи
4) к пункту 2


--------------------
Ошибка не становится истиной по причине широкого распространения,
как и Истина не становится Ошибкой из-за того, что никто её не видит.
PM   Вверх
StealeR
Дата 27.9.2014, 16:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 37
Регистрация: 14.11.2005

Репутация: нет
Всего: нет



Цитата(drkot @ 27.9.2014,  00:55)
Цитата(StealeR @  25.9.2014,  18:21 Найти цитируемый пост)
заранее неизвестное N-ое кол-во записей

что мешает передавать количество?...

дело в том, что общее кол-во передаваемых записей мне было как раз известно. В единице экземпляра записи передаются значения "всего" и "текущая запись", неизвестно было кол-во записей в одной конкретной пересылке.

пока сидел без инета, решил проблему другим путем - из необходимого набора записей на сервере формирую ответ в формате JSON и пересылаю его через Socket.SendText. На клиенте делаю ReceiveText и разбираю JSON обратно в массив записей. Работает на ура. 

Алексей, еще раз спасибо за Ваш "велосипед", на досуге по-разбираюсь. Надеюсь, еще пригодится smile

Это сообщение отредактировал(а) StealeR - 27.9.2014, 16:37
PM MAIL   Вверх
kami
Дата 27.9.2014, 17:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 6
Всего: 72



Цитата(StealeR @  27.9.2014,  16:37 Найти цитируемый пост)
пересылаю его через Socket.SendText. На клиенте делаю ReceiveText и разбираю JSON обратно в массив записей. Работает на ура.

Это хорошо, только опять не учитываете 3 момента:
1. SendText - функция. Она возвращает, сколько байт реально отправлено. Отсюда вывод - может быть отправлен не весь текст, а только его часть. Оставшееся надо "досылать", когда буфер сокета освободится (и опять - возможно, отправится не весь остаток).
2. SendText (по крайней мере до Delphi2010) использует AnsiString, а не UnicodeString. Могут возникнуть проблемы с не-английскими символами.
3. Используя JSON Вы увеличили объем передаваемых данных. Соответственно - увеличилась вероятность того, что на приемный конец в событие OnRead придет только часть данных, а оставшиеся - в другом (или даже других) OnRead.

К сожалению, при тестировании такие проблемы обычно не всплывают.
А в условиях боевой эксплуатации отловить подобное будет сложно, и ошибки скорее всего будете искать в другом месте.

Чтобы убедиться, что всё работает правильно, проще всего в цикле отправить пару тысяч записей. Этим вы, возможно, добъетесь того, что заполнится буфер на передачу и воспроизведете момент №1, а приемный буфер склеит между собой несколько отправок (вполне возможно - разрезав где-нибудь посредине. Как пример - отправляете "123" и "456", а приходит "1234" и "56") - момент №3.

Добавлено через 1 минуту и 31 секунду
Еще раз повторюсь - при тестировании всё будет идеально, единичные сбои не в счет - их не воспроизвести.
А при переходе на боевой режим работы вы подобное не отловите и не сможете понять, в чем проблема.
PM MAIL WWW   Вверх
drkot
Дата 27.9.2014, 20:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ищущий
***


Профиль
Группа: Завсегдатай
Сообщений: 1042
Регистрация: 5.5.2006

Репутация: 0
Всего: 8



Цитата(kami @  27.9.2014,  18:30 Найти цитируемый пост)
цикле отправить пару тысяч записей

если делать в пределах локалхост, то проблемы пересылки никогда не проявятся, ее попросту нет.

а чтобы не проблем было меньше лучше пользоваться готовыми надстройками типа indy или альтернатив.


--------------------
Ошибка не становится истиной по причине широкого распространения,
как и Истина не становится Ошибкой из-за того, что никто её не видит.
PM   Вверх
kami
Дата 27.9.2014, 21:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 6
Всего: 72



Цитата(drkot @  27.9.2014,  20:32 Найти цитируемый пост)
если делать в пределах локалхост, то проблемы пересылки никогда не проявятся, ее попросту нет.

Это опять-таки частный случай, выполняемый в силу особенностей реализации внутренностей Windows. Полагаться на эту недокументированную возможность не стоит.
И - такое поведение избавляет от разбиения пакетов, но никак не от их склейки.
PM MAIL WWW   Вверх
StealeR
Дата 29.9.2014, 10:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 37
Регистрация: 14.11.2005

Репутация: нет
Всего: нет



kami, я с Вами полностью согласен относительно вероятности недополучения информации, и возможно, придется с этим столкнуться. Временно (поскольку поджимают сроки) обошел вероятность возникновения этой проблемы обработкой полученной информации на клиенте путем проверки корректности JSON-строки. Если движок считает строку корректной - принимаем и парсим согласно алгоритму. На текущий момент с рабочей среде (~100 станций в сети, между сервером и клиентом 2 свитча - 1 дорогой в серверной, 1 дешевый в кабинете) сервер и клиент работают стабильно.

Это сообщение отредактировал(а) StealeR - 29.9.2014, 10:47
PM MAIL   Вверх
StealeR
Дата 14.10.2014, 07:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 37
Регистрация: 14.11.2005

Репутация: нет
Всего: нет



kami, все-таки дошли руки-ноги до необходимости использовать Ваши наработки. Небольшой косячок в архиве обнаружился - не приложен файл uCommonFunctions.pas, который в процессе отладки был заменен на Math smile
PM MAIL   Вверх
StealeR
Дата 17.10.2014, 10:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 37
Регистрация: 14.11.2005

Репутация: нет
Всего: нет



kami, благодарю за предоставленный "велосипед", едет на ура smile
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Сети"
Snowy
Poseidon
MetalFan

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делится вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи

Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Snowy, Poseidon, MetalFan.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.0830 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.