【问题标题】:Reading TidTCPServer incoming data until disconnect读取 TidTCPServer 传入数据直到断开连接
【发布时间】:2019-06-09 01:19:52
【问题描述】:

我正在尝试从 Delphi 的 TTCPserver 迁移到 Indy 的 TidTCPServer (Delphi XE10.2),但我无法弄清楚如何在 Execute 中读取传入数据,我发现的所有示例都是使用需要“ETX”的 readln “字符。而且我还没有找到接收到的字节数或任何长度。

那么我如何阅读完整的包? 我想象过类似的东西; 读取字节直到“断开连接”

我在下面尝试了这个(虽然不是同时),我没有得到任何包或字节,除非我使用 telnet 客户端并从键盘键入字符 - 这将逐字节地给我数据。但是从未出现过来自“第 3 方客户端”的包裹。我看到客户端再次连接和断开连接。

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;

【问题讨论】:

    标签: indy10


    【解决方案1】:

    您收到的消息有一个结构 - 一个 4 字节的消息 ID,后跟一个指定消息数据长度的 4 字节 ASCII 字符串,然后是实际的消息数据。

    您的TTCPServer 代码遵循该结构,但您的TIdTCPServer 代码不是(甚至不接近!)。如果有的话,Indy 让这种工作变得简单,因为 Indy 的 TIdIOHandler 类有许多方法可用于读取各种数据格式,而 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;
    

    【讨论】:

    • 非常感谢 Remy,我很抱歉我的问题并不清楚,我并没有试图展示一个完全迁移的过程 - 我只是想收到一些东西作为开始。我什么也没收到,但我找到了原因——我使用了一个我找到的示例项目,并在 OnConnect msgToClient 中有这个 := 'Welcome ' + typeClient + ' ' + 'Client :)'; AContext.Connection.IOHandler.WriteLn(msgToClient);客户端当然不接受(不是协议),它会断开连接并导致 #10053 异常。删除后,我开始看到传入的数据。
    • 无论如何,评论中的字符都用完了 - 迁移到 Indy 并摆脱 TTCPServer 和 TTCPClient 似乎是一项简单的任务 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-15
    相关资源
    最近更新 更多