【问题标题】:Indy TCPServer doesn't receive all packages from TCPClient when sending packages too frequentlyIndy TCPServer 在发送包过于频繁时不会收到来自 TCPClient 的所有包
【发布时间】:2012-02-07 09:47:15
【问题描述】:

我遇到了 Indy TCP 连接问题。我使用带有 Indy 10 的 Turbo Delphi 2006。 我想将多个 TCP 包从 idTCPClient 发送到 idTCPServer。

当我只想发送一个包,或者我在函数的两次调用之间插入一个 sleep(100) 命令时,它工作得很好。但是如果我调用这个函数过于频繁,它不会每次都调用服务器的onExecute。

我的发送代码:

procedure SendData(var data: TIdBytes) ;
begin
  FormMain.IdTCPClient.Connect ;
  FormMain.IdTCPClient.Socket.Write(data);
  FormMain.IdTCPClient.Disconnect ;
end ;

我多次调用此函数(每秒 5-10 次),并希望在我的服务器应用程序中处理所有这些包:

procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext);
var
  data: TIdBytes ;
begin
  AContext.Connection.IOHandler.ReadBytes(data, 4, false)
  // processing data
end

提前感谢您的回答!

【问题讨论】:

    标签: delphi tcpclient indy indy10 tcpserver


    【解决方案1】:

    每次调用Connect() 时,您都在创建一个新连接,TIdTCPServer 将启动一个新线程来处理该连接(除非您启用线程池)。那是你真正想要的吗?让客户端将连接保持打开一段时间并尽可能重用现有连接会更有效。仅在您确实不再需要时才断开连接,例如空闲一段时间。在两端建立新连接是一项昂贵的操作,因此您应该尽可能减少这种开销。

    在客户端,当您调用Write(data) 时,它会发送整个TIdBytes,但您不会将TIdBytes 的长度发送到服务器,因此它知道需要多少字节。 TIdIOHandler.Write(TIdBytes) 不会为您执行此操作,您必须手动执行。

    在服务器端,您告诉ReadBytes() 一次只能读取 4 个字节。在每个 4 字节块之后,您将退出 OnExecute 事件处理程序并等待再次调用它以读取下一个 4 字节块。除非客户端源 TIdBytes 的长度是 4 的偶数倍,否则 ReadBytes() 在尝试读取客户端的最后一个小于 4 字节的块时会引发异常(导致服务器断开连接),所以您的服务器代码不会收到该块。

    试试这个:

    procedure SendData(var data: TIdBytes) ; 
    begin 
      FormMain.IdTCPClient.Connect; 
      try
        FormMain.IdTCPClient.IOHandler.Write(Longint(Length(data))); 
        FormMain.IdTCPClient.IOHandler.Write(data); 
      finally
        FormMain.IdTCPClient.Disconnect; 
      end;
    end; 
    
    procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext); 
    var 
      data: TIdBytes; 
    begin 
      with AContext.Connection.IOHandler do
        ReadBytes(data, ReadLongint, false);
      // process data 
    end;
    

    话虽如此,如果出于某种原因更改客户端代码以发送TIdBytes 长度不是一个选项,那么请改用此服务器代码:

    procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext); 
    var 
      LBytes: Integer;
      data: TIdBytes; 
    begin 
      // read until disconnected. returns -1 on timeout, 0 on disconnect
      repeat until AContext.Connection.IOHandler.ReadFromSource(False, 250, False) = 0;
      AContext.Connection.IOHandler.InputBuffer.ExtractToBytes(data);
      // process data 
    end;
    

    或者:

    procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext); 
    var 
      strm: TMemoryStream;
      data: TIdBytes;
    begin 
      strm := TMemoryStream.Create;
      try
        // read until disconnected
        AContext.Connection.IOHandler.ReadStream(strm, -1, True);
        strm.Position := 0;
        ReadTIdBytesFromStream(strm, data, strm.Size);
      finally
        strm.Free;
      end;
      // process data 
    end;
    

    或者:

    procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext); 
    var 
      strm: TMemoryStream;
    begin 
      strm := TMemoryStream.Create;
      try
        // read until disconnected
        AContext.Connection.IOHandler.ReadStream(strm, -1, True);
        // process strm.Memory up to strm.Size bytes
      finally
        strm.Free;
      end;
    end;
    

    【讨论】:

    • 感谢您的回答!我已将这些包合并为一个大包,并将其作为流读取,现在它可以工作了。谢谢!
    • 如此精彩的答案! Indy 是 Delphi 背后 40% 的权重,Remy 是这个库的主要作者。从局外人的角度来看,它是纯粹的天才。人类卓越本身!
    猜你喜欢
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-31
    • 2013-02-10
    • 2021-10-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多