Конференция "Сети" » ServerSocket в режиме ThreadBlocking [D7]
 
  • Marchelo2012 © (13.02.13 14:22) [0]
    Добрый день, уважаемый программисты! Опишу вкратце решаемую задачу.
    Необходим сервер приема данных (текстовые сообщения в 15-20 строк). Данные отправляют клиенты (устройства, которые

    связываются с сервером по каналу  GPRS). Всего их 2000-2500 шт., каждый отправляет сообщение 1 раз в 5 минут,

    соответственно на сервер примерно 5-20 обращений в секунду. Принятое сообщение необходимо поместить в Memo. Вроде бы задача

    в 3 строки, но корректно настроить работу сервера не удается. Пошел сначала простым и неправильным путём:
    1. ServerSocket в режиме stNonBlocking
    [code=php]procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
         Socket: TCustomWinSocket);
       begin
         {От клиента получено сообщение - выводим его в Memo1}
         Memo2.Lines.Insert(0,'Message received from client');
         Memo1.Lines.Insert(0,'> '+Socket.ReceiveText);
         if RadioButton1.Checked then begin
         ServerSocket1.Socket.Connections[0].SendText('OK');
         ServerSocket1.Socket.Connections[0].Close;
         end;
       end;
    В таком режиме при появлении события посылки сообщения от клиента, сообщение записывается в Memo. Но, естественно, новое

    соединение появляется раньше, чем происходит запись принятого текста.
    То есть все сообщения принимаются, но принимаются они "урезанными". Ах да, забыл написать, что клиенту после приема от него

    сообщения надо отправить "OK", чтобы устройство знало, что пакет принят и можно отключаться.
    2. Было принято решение использовать сервер (как вы уже догадались=)) в режиме stThreadBlocking. При небольшом количестве

    клиентов все работает прекрасно (вообщем то как и первый вариант), но при количестве 2000-2500 клиентов сообщения в Memo

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

    код:

    //описываем класс
    type
       TServerThread = class(TServerClientThread)
       procedure ClientExecute; override;
     end;
    //описываем процедуру, которая будет срабатывать при запуске "нити"
    procedure TServerThread.ClientExecute;
    var i:integer;
    begin
       Form1.Memo1.Lines.Insert(0,'> '+ClientSocket.ReceiveText);
       ClientSocket.SendText('OK');
       ClientSocket.Close;
       Terminate;
    end;
    ...
    procedure TForm1.ServerSocket1GetThread(Sender: TObject;
         ClientSocket: TServerClientWinSocket;
         var SocketThread: TServerClientThread);
       begin
         Memo2.Lines.Insert(0,'Get Thread');
         //создаем экземпляр класса с приоритетом реального времени
         SocketThread:=TServerThread.Create(true, ClientSocket);
         SocketThread.Priority:=tpTimeCritical;
         SocketThread.Resume;
       end;


    Подскажите, как модернизировать код, чтобы сервер справлялся с поставленной задачей? В процедуре

    TServerThread.ClientExecute нужно что-то дописать, чтобы сервер ждал полного сообщения от клиента, а уже потом отсылал ему

    "OK" и закрывал соединение. Финальная строка в тексте, полученном от клиента, всегда начинается с "REND"...

    p.s. не забываем, что клиенты работают по gprs, соединение соответственно не "самолёт"... Заранее спасибо!
  • Сергей М. © (13.02.13 15:23) [1]

    > новое соединение появляется раньше, чем происходит запись принятого текста


    Оно никак не может появиться раньше, потому что в режиме NonBlocking события возбуждаются последовательно в одном-единственном потоке - том который активировал сервер.

    Попросту говоря, если на сервере возникло событие доступности для чтения данных, то вызванный при этом его обработчик OnClientRead будет выполнен полностью, т.е. не прерван обработчиком иных потенциально возникающих в этот момент событий, включая OnClientConnect. Естественно при условии что в теле обработчика события OnClientRead нет явных или неявных обращений к диспетчеру оконных сообщений приложения а-ля Application.ProcessMessaqges.


    > принимаются они "урезанными"


    Ты просто не дожидаешься полного сообщения, ибо получаешь первую его порцию и тут же рвешь соединение.


    > Принятое сообщение необходимо поместить в Memo


    Ради только этого нет никакого видимого резона городить огород с ThreadBlocking
  • Marchelo2012 © (13.02.13 15:29) [2]

    > Ты просто не дожидаешься полного сообщения, ибо получаешь
    > первую его порцию и тут же рвешь соединение.

    Что же мне сделать, чтобы дождаться?..
  • Marchelo2012 © (13.02.13 15:39) [3]

    > Ради только этого нет никакого видимого резона городить
    > огород с ThreadBlocking

    Это просто для теста. Чтобы убедиться, что все пакеты от устройств принимаются и принимаются полностью. А так в дальнейшем принятый текст будет передаваться в другой поток, где он будет парситься, а готовые значения укладываться в БД.
  • Сергей М. © (13.02.13 15:53) [4]

    > Что же мне сделать, чтобы дождаться?


    Остудить на время кодерский ура-энтузиазм, сесть за стол, взять бумагу и ручку и разработать-утвердить протокол информационного сообщения между сервером и его клиентами.

    Ибо в данный момент совершенно неясно как разграничиваются строки, передаваемые между сервером и клиентами.
    Вариантов разграничений всего два: передача префиксной или постфиксной служебной инф-ции.
    Префиксом может быть, например, длина следующей за ним строки (без терминирующего постфикса)
    Постфиксом может быть один или несколько символов, никогда не встречающихся ни в одной из передаваемых строк, например, NUL или CR или LF или CRLF.
  • Marchelo2012 © (13.02.13 16:08) [5]
    $PLIO,XXXXXXXX,T3.4318,00003803,130213,155637,00,0000,0000,00,00,1F32,0133 ,0*3A
    $PLTA,XXXXXXXX,+22.00,,,,22.3,0.00,0.00,0.00,0.00*14
    ...
    ...
    ...
    $REND,XXXXXXXX,00003803,01,00,1F32,00,00,44,44,26,00,00,1D,00,00,00,00,0983*14


    Вот как выглядит пакет от УСПД. Постфиксом в данном случае является $REND в последней строке. Вот как по нему нужно определить, что пакет принят и послать устройство "OK", чтобы она знало, что пакет успешно принят сервером и отключилось? Запустить бесконечный цикл и, например, функцией pos проверять наличие $REND в строке? А в теле цикла организовать что то вроде:
    buff:=buff+ClientSocket.ReceiveText ?
    Вот как это сделать, ума не приложу. Вроде все просто... Куча примеров на форумах и в статьях, как организовать прием файлов в отдельном потоке. С текстом должны быть еще проще, а что-то не соображу... Подскажи, пожалуйста, вот этот момент.
  • Сергей М. © (13.02.13 16:15) [6]

    > Вот как выглядит пакет от УСПД


    Где он так "выглядит" ?
    И почему не проштудировать документацию по протоколу инф.обмена с этим самым "УСПД" ?


    > Постфиксом в данном случае является $REND в последней строке


    Какой же это постфикс, если за ним следует еще куча символов ?
  • Marchelo2012 © (13.02.13 16:51) [7]

    > Где он так "выглядит" ?
    > И почему не проштудировать документацию по протоколу инф.
    > обмена с этим самым "УСПД" ?

    УСПД формирует txt файл вот с таким содержанием, которое я привел выше. Этот txt файл передается на сервер (где я и буду запускать свое приложение). Документации на УСПД нет. Разработчик УСПД сидит в соседнем кабинете. Сервер, написанный на delphi существует и работает несколько лет. Вот он как то определяет по $REND и исправно получает пакеты целиком и без мусора... Узнать алгоритм нет возможности, но появилась необходимость в написание альтернативы данного сервера.
  • Сергей М. © (13.02.13 16:59) [8]

    > Узнать алгоритм нет возможности


    Но наверняка есть возможность средствами сетевого снифеера получить полный и точный дамп сеанса прикладного инф.обмена между "УСПД" и существующим сервером.


    > Этот txt файл передается на сервер


    Кто его передает - святой дух что ли ?
    Или все-таки тот самый "УСПД", разработчик которого в двух шагах от тебя ?
    Если "УСПД", то разработчик, полагаю, должен знать подробности прикл.протокола инф.обмена между его детищем и сервером ..
  • Slym © (14.02.13 08:29) [9]
    троки случайно не CRLF кончаются...
    s:=readln;
    if Starts(s,'$REND') then
    begin
    Sendln('OK');
    Close;
    end;
 
Конференция "Сети" » ServerSocket в режиме ThreadBlocking [D7]
Есть новые Нет новых   [118639   +41][b:0][p:0.001]