delphi как настроить tcpserver для получения строковых данных - PullRequest
1 голос
/ 04 апреля 2019

Мне нужно настроить службу TCP для обработки запроса некоторых клиентов

все запросы поступают в виде шестнадцатеричных пакетов длиной 1099 байт, а также все начинаются с 00D0 и заканчиваются 00000000

procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
begin
  AContext.Connection.IOHandler.ReadBytes(data, 1099, False);

    RStr:='';
    for I := 0 to length(data)-1 do
      RStr := RStr + chr(data[i]);

    if(copy(RStr,1,4)='00D0') and (copy(RStr,1091,8)='00000000') then
      begin
       Memo14.Lines.Add( 'Frame added to database.' );
      end
    else
      begin
       Memo14.Lines.Add( 'error invalid Frame ...' );
      end;
end;

сервер получает данные размером 1099 байтов, но только error invalid Frame ... показывает.

в чем моя проблема с кодом!?

PS: клиент непрерывно отправляет данные на сервер, я имею в виду, что клиент получает данные от третьей стороны и отправляет их на сервер, так что возможно, что данные не запускаются с первого пакета! поэтому я должен сбросить некоторые данные, чтобы сначала получить пакет 00D0!

1 Ответ

7 голосов
/ 04 апреля 2019

Indy имеет функцию BytesToString() в единице IdGlobal, поэтому вам не нужно конвертировать TIdBytes в string вручную:

RStr := BytesToString(data);

A string обычно индексируется 1 (если вы не компилируете для мобильных устройств и не используете {$ZEROBASEDSTRINGS OFF}), поэтому copy(RStr,1091,8) следует использовать 1092 вместо 1091, так как вы читаете 1099 байт вместо 1098 байт:

copy(RStr,1092,8)

Но Indy имеет функции TextStartsWith() и TextEndsWith(), также в модуле IdGlobal, поэтому вам не нужно извлекать и сравнивать подстроки вручную:

if TextStartsWith(RStr, '00D0') and TextEndsWith(RStr, '00000000') then

Теперь, как говорится, если ваши данные сокетов действительно текстовые по природе, а не двоичные, вы должны использовать метод TIdIOHandler.ReadString() вместо метода TIdIOHandler.ReadBytes():

RStr := AContext.Connection.IOHandler.ReadString(1099);

В качестве альтернативы, TIdIOHandler также имеет WaitFor() и ReadLn() методы для чтения текста с разделителями, например:

AContext.Connection.IOHandler.WaitFor('00D0');
RStr := '00D0' + AContext.Connection.IOHandler.ReadLn('00000000') + '00000000';

или

AContext.Connection.IOHandler.WaitFor('00D0');
RStr := '00D0' + AContext.Connection.IOHandler.WaitFor('00000000', True, True);

Наконец, TIdTCPServer является многопоточным компонентом, его событие OnExecute запускается в контексте рабочего потока, а не основного потока пользовательского интерфейса. Таким образом, вы ДОЛЖНЫ синхронизироваться с основным потоком пользовательского интерфейса при доступе к пользовательскому интерфейсу, например, с помощью методов класса RTL TThread.Queue() или TThread.Synchronize(), классов Indy TIdNotify или TIdSync и т. Д. Плохие вещи могут и обычно do случаются при доступе к элементам управления UI снаружи основного потока UI.

ОБНОВЛЕНИЕ : в комментариях вы говорите, что данные на самом деле в байтах, а не в текстовых символах. И что вам нужно сбросить байты, прежде чем вы начнете читать записи. В этом случае вам не следует конвертировать байты в string. Вместо этого обрабатывайте байты как есть, например:

procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
var
  data: TIdBytes;
  b: Byte;
begin
  b := AContext.Connection.IOHandler.ReadByte;
  repeat
    if b <> $00 then Exit;
    b := AContext.Connection.IOHandler.ReadByte;
  until b = $D0;

  SetLength(data, 2);
  data[0] = $00;
  data[1] = $D0;
  AContext.Connection.IOHandler.ReadBytes(data, 1097, True);

  repeat
    if {(PWord(@data[0])^ = $D000)}(data[0] = $00) and (data[1] = $D0)
      and (PUInt32(@data[1091])^ = $00000000) then
    begin
      TThread.Queue(nil,
        procedure
        begin
          Memo14.Lines.Add( 'Frame added to database.' );
        end;
      );
    end else
    begin
      TThread.Queue(nil,
        procedure
        begin
          Memo14.Lines.Add( 'error invalid Frame ...' );
        end;
      );
    end;
    AContext.Connection.IOHandler.ReadBytes(data, 1099, False);
  until False;
end;
...