Чтение входящих данных TidTCPServer до отключения - PullRequest
0 голосов
/ 22 мая 2019

Я пытаюсь мигрировать с Delphi TTCPserver на Indy TidTCPServer (Delphi XE10.2), но я не могу понять, как читать входящие данные в Execute, все примеры, которые я нашел, используют readln, для которого требуется ETX "char. И я не нашел, чтобы получить полученные байты или длину.

Так как же мне прочитать полный пакет? Я представлял что-то вроде; чтение байтов до "отключения"

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

procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
    Port          : Integer;
    PeerPort      : Integer;
    PeerIP        : string;
    msgFromClient : string;
    msgToClient   : string;
    buf : TidBytes;
    l :integer;
    ABufStream : TMemoryStream;
begin
// this doesn't return anything
    ABufStream := TMemoryStream.Create;
    try
     //       AContext.Connection.IOHandler.ReadStream(ABufStream, -1, True);
      AContext.Connection.IOHandler.InputBufferToStream(ABufStream, -1);
      ABufStream.Position := 0;
      ABufStream.WriteBuffer(buf, ABufStream.Size);
      msgFromClient := format('received %d bytes',[ABufStream.Size]);
    finally
       ABufStream.Free;
    end;

// this doesn't return anything neither
    AContext.Connection.IOHandler.ReadBytes(buf, -1, False);
    l := length(buf);
    if l > 0 then
      Display('CLIENT', '(Bytes =' + IntToStr(l));
end;

Данные, которые я получаю в «массиве байтов», где нет ETX (или конца пакета), как он работает, выглядит следующим образом

  1. Клиент подключается (запускает tcp-сессию)
  2. Клиент отправляет пакет из N байтов
  3. Клиент отключается (конец сеанса TCP)

Пакет содержит заголовок и длину части данных в первых 8 байтах.

Мой старый код из TTCPServer выглядит так:

procedure TDispatchScanThread.TCPServerOnAccept(Sender: TObject; ClientSocket: TCustomIpClient);
var
  s: ShortString;
  l: integer;
  Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
  ADispatchPacket: TDispatchPacket;
  AQueuedStatus : Boolean;

begin
  try
    LogQueue.AddToLog(format('TCPServer receiving (onAccept)',[]), llvVerbose);
    ZeroMemory(@Buf, MAX_DATAPACKET);
    l := ClientSocket.PeekBuf(Buf, 8);
    if (l <> SOCKET_ERROR) and (l = 8) then
    begin
      s := '0000';       // check if IVD version header is valid for us
      Move(Buf[0], s[1], 4);
      if not(s = sHeaderID) then
        raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);

      s := '0000';       // Fetch data package length
      Move(Buf[4], s[1], 4);
      l := StrToInt(s);
      ClientSocket.ReceiveBuf(Buf[0], l + HEADER_SIZE);       // total length is header + data

      // Create the dispatch packet object, move the data to the buffer and queue it.
      ADispatchPacket := TDispatchPacket.Create;
      ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
      ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens

      AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
      LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);
    end else
        begin
          LogQueue.AddError(format('TCPServer socket error: %d',[l]));
        end;
  except
    on E: Exception do
    begin
      LogQueue.AddError(format('TcpServerAccept Error: %s',[E.Message]));
      FreeAndNil(ADispatchPacket);
    end;
  end; // except
end;

1 Ответ

1 голос
/ 23 мая 2019

Сообщения, которые вы получаете, имеют структуру - 4-байтовый идентификатор сообщения, за которым следует 4-байтовая строка ASCII, указывающая длину данных сообщения, а затем фактические данные сообщения.

Ваш *Код 1003 * соответствует этой структуре, а ваш код TIdTCPServer - нет (даже близко!).Во всяком случае, Indy облегчает такую ​​работу, потому что в классе TIdIOHandler в Indy есть много методов, доступных для чтения любых форматов данных, тогда как TTCPServer не предлагает ничего, что могло бы вам помочь, вы должны прочитать и проанализировать всевручную.

В этом случае вы можете использовать методы TIdIOHandler.ReadString() и TIdIOHandler.ReadBytes(), например:

procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
  s: string;
  l: integer;
  Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
  ADispatchPacket: TDispatchPacket;
  AQueuedStatus : Boolean;
  IdBuf: TIdBytes;
begin
  LogQueue.AddToLog(format('TCPServer receiving (onExecute)',[]), llvVerbose);
  ZeroMemory(@Buf, MAX_DATAPACKET);

  // check if IVD version header is valid for us
  s := AContext.Connection.IOHandler.ReadString(4);
  if s <> sHeaderID then
    raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);
  Move(ShortString(s)[1], Buf[0], 4);

  s := AContext.Connection.IOHandler.ReadString(4);
  l := StrToInt(s);
  Move(ShortString(s)[1], Buf[4], 4);

  AContext.Connection.IOHandler.ReadBytes(IdBuf, l);
  Move(PByte(IdBuf)^, Buf[8], l);

  // Create the dispatch packet object, move the data to the buffer and queue it.
  ADispatchPacket := TDispatchPacket.Create;
  try
    ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
    ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens

    AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
  except
    ADispatchPacket.Free;
    raise;
  end;

  LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);

  AContext.Connection.Disconnect;
end;

procedure TForm2.IdTCPServerException(AContext: TIdContext; AException: Exception);
begin
  LogQueue.AddError(format('TcpServer Error: %s',[AException.Message]));
end;

, или вы можете использовать метод TIdIOHandler.ReadStream(), например:

procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
  s: ShortString;
  l: integer;
  Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
  ADispatchPacket: TDispatchPacket;
  AQueuedStatus : Boolean;
  ABufStream : TIdMemoryBufferStream;
begin
  LogQueue.AddToLog(format('TCPServer receiving (onExecute)',[]), llvVerbose);
  ZeroMemory(@Buf, MAX_DATAPACKET);

  ABufStream := TIdMemoryBufferStream.Create(@Buf, SizeOf(Buf));
  try
    AContext.Connection.IOHandler.ReadStream(ABufStream, 8, False);

    // check if IVD version header is valid for us
    s := '0000';       // check if IVD version header is valid for us
    Move(Buf[0], s[1], 4);
    if s <> sHeaderID then
      raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);

    s := '0000';       // Fetch data package length
    Move(Buf[4], s[1], 4);
    l := StrToInt(s);
    AContext.Connection.IOHandler.ReadStream(ABufStream, l, False);

    // Create the dispatch packet object, move the data to the buffer and queue it.
    ADispatchPacket := TDispatchPacket.Create;
    try
      ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
      ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens

      AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
    except
      ADispatchPacket.Free;
      raise;
    end;
  finally
    ABufStream.Free;
  end;

  LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);

  AContext.Connection.Disconnect;
end;

procedure TForm2.IdTCPServerException(AContext: TIdContext; AException: Exception);
begin
  LogQueue.AddError(format('TcpServer Error: %s',[AException.Message]));
end;

В качестве альтернативы:

procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
  s: ShortString;
  l: integer;
  Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
  ADispatchPacket: TDispatchPacket;
  AQueuedStatus : Boolean;
  ABufStream : TIdMemoryBufferStream;
begin
  LogQueue.AddToLog(format('TCPServer receiving (onExecute)',[]), llvVerbose);
  ZeroMemory(@Buf, MAX_DATAPACKET);

  ABufStream := TIdMemoryBufferStream.Create(@Buf, SizeOf(Buf));
  try
    AContext.Connection.IOHandler.ReadStream(ABufStream, -1, True);

    if ABufStream.Size < 8 then
      raise Exception.CreateFmt('Invalid dispatch packet Header size %d', [ABufStream.Size]);

    // check if IVD version header is valid for us
    s := '0000';       // check if IVD version header is valid for us
    Move(Buf[0], s[1], 4);
    if s <> sHeaderID then
      raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);

    s := '0000';       // Fetch data package length
    Move(Buf[4], s[1], 4);
    l := StrToInt(s);

    if ABufStream.Size < (l + HEADER_SIZE) then
      raise Exception.CreateFmt('Invalid dispatch packet size %d, expected %d', [ABufStream.Size, l + HEADER_SIZE]);

    // Create the dispatch packet object, move the data to the buffer and queue it.
    ADispatchPacket := TDispatchPacket.Create;
    try
      ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
      ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens

      AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
    except
      ADispatchPacket.Free;
      raise;
    end;
  finally
    ABufStream.Free;
  end;

  LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);
end;

procedure TForm2.IdTCPServerException(AContext: TIdContext; AException: Exception);
begin
  LogQueue.AddError(format('TcpServer Error: %s',[AException.Message]));
end;
...