【问题标题】:Indy TCP - Read data in a loopIndy TCP - 循环读取数据
【发布时间】:2012-06-08 13:10:01
【问题描述】:

TCP 服务器每 8 毫秒连续发送数据帧。我想编写一个能够接收这些数据帧的客户端。 Indy 9中是否有任何程序可以知道缓冲区中是否有可用数据?

我当前的程序如下(我正在使用线程):

procedure TThreadRead.Execute;
var
  buffer: array [0..755] of byte;
  //s1: string;
  //i: integer;
begin
  IdTCPClient1.RecvBufferSize:= 756;
  IdTCPClient1.Connect;
  while Terminated = false do
  begin
    if IdTCPClient1.InputBuffer.Size = 0 then
       IdTCPClient1.ReadFromStack(True,0,False);
    while IdTCPClient1.InputBuffer.Size > 0 do
    begin
       ReadBuffer(buffer, FClient.InputBuffer.Size);
       //s1:= '';
       //For i:=0 To Length(buffer)-1 Do
       //  s1:=s1+IntToHex(Ord(buffer[i]),2); //Read values-->global var
       //Form1.Memo1.Text:=s1;
    end;
  end;
end;

有没有更高效的连续读取 TCP 数据的解决方案(如 UDP 中的 onread 事件)?

提前致谢。

【问题讨论】:

    标签: tcp tcpclient indy indy-9


    【解决方案1】:

    TIdTCPClient 不是异步组件。它不会告诉您数据何时到达。您需要使用 Timer 或 Thread 定期轮询套接字以获取新数据(TIdUDPServer 使用内部线程来触发其OnUDPRead 事件),例如:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      IdTCPClient1.Connect;
      Timer1.Enabled := True;
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    begin
      Timer1.Enabled := False;
      IdTCPClient1.Disconnect;
    end;
    
    procedure TForm1.Timer1Timer(Sender: TObject);
    var
      s1: string;
    begin
      s1 := IdTCPClient1.CurrentReadBuffer;
      ...
    end;
    

    话虽如此,CurrentReadBuffer() 通常不是最好的选择。通常你会做更多这样的事情:

    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
      Timer1.Enabled := False;
    
      IdTCPClient1.ReadFromStack(True, 0, False);
    
      while IdTCPClient1.InputBuffer.Size > 0 do
      begin
        // read one complete frame and process as needed ...
      end;
    
      Timer1.Enabled := True;
    end;
    

    更新:给出有关框架结构和切换到线程的新信息,您应该改为这样做:

    procedure TThreadRead.Execute;
    var
      buffer: array of Byte;
      numbytes: Integer;
    begin
      SetLength(buffer, 0);
      IdTCPClient1.Connect;
      try
        while not Terminated do
        begin
          numbytes := StrToInt('$' + IdTCPClient1.ReadString(8)) - 8;
          if numbytes <> Length(buffer) then
            SetLength(buffer, numbytes);
          if numbytes > 0 then
            IdTCPClient1.ReadBuffer(buffer[0], numbytes);
          // process buffer up to numbytes as needed...
        end;
      finally
        IdTCPClient1.Disconnect;
      end;
    end;
    

    【讨论】:

    • 我做错了,因为你的代码不工作......为什么当 inputbuffer.size = 0 时你从堆栈中读取?为什么 CurrentReadBuffer() 不是最好的选择? TidTCPServer 是异步组件吗?也许使用 TCP 服务器读取数据更容易...
    • Indy 不会从套接字(即堆栈)读取数据,直到您告诉它读取某些内容。当计时器触发时,它会检查InputBuffer 是否为空,如果是,则调用ReadFromStack() 以读取当前套接字中的任何数据(如果有),并将其放入InputBuffer。在数据首先到达InputBuffer 之前,代码不会尝试处理任何帧,这表明有一些东西可以处理。如果您的InputBuffer 始终为空,即使在调用ReadFromStack() 之后也是如此,那么服务器根本不会向您的客户端发送任何数据。
    • 您不能使用TIdTCPServer 与另一台服务器通信。为此,您必须使用TIdTCPClient
    • 如果您的服务器真的每 8 毫秒发送一次数据帧,那么执行 InputBuffer.Size 检查根本没有任何好处。只需立即调用ReadFromStack(),然后读取InputBuffer 中存在的尽可能多的帧,然后将计时器间隔设置得尽可能低(最好使用单独的线程,因为TTimer 不能以8ms 运行精确)。当帧之间的延迟较长时,执行我展示的那种Size 检查会更有益。
    • 我已经相应地更新了我的答案,尽管早期的InputBuffer.Size 检查代码应该可以正常工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-14
    • 2011-03-22
    • 1970-01-01
    相关资源
    最近更新 更多