Serversocket В Режиме Stthreadblocking

Тема в разделе "Delphi - Сети", создана пользователем Marchelo2012, 13 фев 2013.

  1. Marchelo2012

    Marchelo2012 New Member

    Репутация:
    0
    Регистрация:
    13 фев 2013
    Сообщения:
    1
    Симпатии:
    0
    Добрый день, уважаемый программисты! Опишу вкратце решаемую задачу.
    Необходим сервер приема данных (текстовые сообщения в 15-20 строк). Данные отправляют клиенты (устройства, которые связываются с сервером по каналу GPRS). Всего их 2000-2500 шт., каждый отправляет сообщение 1 раз в 5 минут, соответственно на сервер примерно 5-20 обращений в секунду. Принятое сообщение необходимо поместить в Memo. Вроде бы задача в 3 строки, но корректно настроить работу сервера не удается. Пошел сначала простым и неправильным путём:
    1. ServerSocket в режиме stNonBlocking
    Код (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 строки от клиента, но этого, само собой, не достаточно. Вот код:
    Код (Delphi):
    //описываем класс
    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, соединение соответственно не "самолёт"... Заранее спасибо!
     
  2. sinkopa

    sinkopa Well-Known Member

    Репутация:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    344
    Симпатии:
    9
    Первый Ваш вариант был "ближе к истине"... но "странности кодирования" присутствуют в обоих...
    (1)Сокет-сервер клиентские сокеты и так отрывает в отдельных нитях...
    (2) Мне кажется, Вы просто не даете клиенту договорить...
    Дело в том что ClientSocket.ReceiveText вовсе не гарантирует что сообщение прочитано полностью...
    Сетевые пакеты могут ведь дробиться (по разным причинам)... тем более Вы говорите что у вас "несколько строк" в сообщении и хреновый канал...
    В общем, Вам нужен признак окончания передачи... ну и читать пока он не появится, либо не произойдет что-то непредвиденное (например у клиента вырубят свет :) )
    Вот, где-то так мне кажется должно быть:
    Код (Delphi):
    uses
    StrUtils;
    //...
    procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
    Socket: TCustomWinSocket);
    var
    s,mes: string;
    mesOk: Boolean;
    connOk: Boolean;
    begin
    { !! Socket: TCustomWinSocket - это сокет клиента !! (у каждого подключившегося он свой) }

    mes := '';
    repeat
    connOk := Socket.Connected;
    if not connOk then // парень отвалился не договорив
    Break;
    s := Socket.ReceiveText;
    mes := mes + s;
    mesOk := (RightStr(mes,8) = '<кончил>');
    until mesOk or (s = '');

    if mesOk then
    begin
    SetLength(mes,Length(mes) - 8); // отрезаем признак конца :-)
    Memo2.Lines.Insert(0,'Пользователь прислал сообщение');
    Memo1.Lines.Insert(0,'> ' + mes);
    if connOk then
    Socket.SendText('OK');
    end
    else
    begin
    Memo2.Lines.Insert(0,'Парень что-то пытался сказать:');
    Memo1.Lines.Insert(0,'> ' + mes);
    if connOk then
    Socket.SendText('ERR');
    end;
    Socket.Close;
    end;

    procedure TForm1.ServerSocket1ClientError(Sender: TObject;
    Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
    var ErrorCode: Integer);
    begin
    ErrorCode := 0; // запретили вывод сообщения об ошибке
    Socket.Close;   // закрыли поломаный сокет
    //  Memo1.Lines.Insert(0,'> ошибко :-( ');
    end;
    А еще (в качестве примечания) в первом Вашем варианте странность...
    Вы читаете данные из сокета клиента который уже что-то уже шлет:
    Код (Delphi):
    Memo1.Lines.Insert(0,'> '+Socket.ReceiveText);
    а потом говорите "Ок" и отрубаете другого клиента (который первым подключился):
    Код (Delphi):
         ServerSocket1.Socket.Connections[0].SendText('OK');
    ServerSocket1.Socket.Connections[0].Close;
    Этот то парень в чем виноват? :)
    Про второй Ваш вариант... я вообще не въехал если честно...
    Единственное, что могу сказать... Вот это
    Код (Delphi):
     Form1.Memo1.Lines.Insert(0,'> '+ClientSocket.ReceiveText);
    должно выполняться Synchronize() однозначно... ну и секции критические, семафоры... без них бардак у Вас полный... в том плане, что не понятно в какой момент какой сокет из какой рожденной нити "пытается" (не скажу что это у него получается) писать в мемо... В общем справку почитайте...
     
Загрузка...

Поделиться этой страницей