【问题标题】:TIdTCPServer OnExecute runs in an infinite loopTIdTCPServer OnExecute 无限循环运行
【发布时间】:2014-01-22 05:51:05
【问题描述】:

TIdTCPServer 组件有问题。我用它来读取远程服务器发送的数据。

下面是我使用的代码:

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
const
  START_PACKET = #11;
  END_PACKET = #10;
var
  IO : TIdIOHandler;
  c  : Char;
  a  : AnsiString;
begin
  a := '';
  IO := AContext.Connection.IOHandler;

  while (IO.InputBuffer.Size > 0) do
  begin
    c := IO.ReadChar;

    if c = START_PACKET then
    begin
      repeat
        c := IO.ReadChar; //(TEncoding.ASCII);
        a := a + c;
      until (c = END_PACKET) or (IO.InputBufferIsEmpty);
    end;
  end;

  if a <> '' then
  begin
    //let's send replay to server
    IO.Write(CreateReply(a));

    //now we need to save what we received to database
    //I use critical section
    try
      EnterCriticalSection(LockDB);

      with DataModule2.results do
      begin
        Close;
        Params[0].AsDateTime := Today;
        Params[1].AsString := a;
        ExecSQL;
      end;
    finally
      LeaveCriticalSection(LockDB);
    end;
  end;
end;

问题是,一旦我的TIdTCPServer 获取了一些数据,它就会启动一个无限循环并占用 100% 的 CPU。

我在这里做错了什么?

【问题讨论】:

    标签: delphi indy


    【解决方案1】:

    一个问题是您实际上从未读取任何数据,因此InputBuffer 将始终为空,a 将始终为空。 OnExecute 事件本身是循环的,因此您没有做任何事情来使其定期产生 CPU 时间片。

    另一个问题是您的逐字符读取和连接效率非常低,并且没有考虑到 SizeOf(Char) 在 Delphi 2009+ 中为 2 或 ReadChar() 可识别 Unicode。

    试试这个:

    procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
    const
      START_PACKET = #11;
      END_PACKET = #10;
    var
      IO : TIdIOHandler;
      a, buf : AnsiString;
      buflen : Integer;
      c : AnsiChar;
    begin
      a := '';
      IO := AContext.Connection.IOHandler;
    
      IO.WaitFor(START_PACKET);
    
      // this is just one example of how to append characters using
      // buffering. use whatever is more comfortable for you...
    
      SetLength(buf, 1024);
      buflen := 0;
    
      repeat
        c := AnsiChar(IO.ReadByte);
        if buflen = Length(buf) then
        begin
          a := a + buf;
          buflen := 0;
        end;
        buf[buflen+1] := c;
        Inc(buflen);
      until (c = END_PACKET) or (IO.InputBufferIsEmpty);
    
      if buflen > 0 then
      begin
        SetLength(buf, buflen);
        a := a + buf;
      end;
      buf := '';
    
      //let's send replay to server
      IO.Write(CreateReply(a));
    
      //now we need to save what we received to database
      //I use critical section
      EnterCriticalSection(LockDB);
      try
        with DataModule2.results do
        begin
          Close;
          Params[0].AsDateTime := Today;
          Params[1].AsString := a;
          ExecSQL;
        end;
      finally
        LeaveCriticalSection(LockDB);
      end;
    end;
    

    或者:

    procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
    const
      START_PACKET = #11;
      END_PACKET = $#10;
    var
      IO : TIdIOHandler;
      a : AnsiString;
      c : AnsiChar;
      i : Integer;
    begin
      IO := AContext.Connection.IOHandler;
    
      IO.WaitFor(START_PACKET);
    
      if IO.InputBufferIsEmpty then
      begin
        IO.CheckForDataOnSource(IdTimeoutDefault);
        IO.CheckForDisconnect;
      end;
    
      i := IO.InputBuffer.IndexOf(END_PACKET);
      if i = -1 then i := IO.InputBuffer.Size;
    
      a := IO.ReadString(i);
    
      if a <> '' then
      begin
        //let's send replay to server
        IO.Write(CreateReply(a));
    
        //now we need to save what we received to database
        //I use critical section
        EnterCriticalSection(LockDB);
        try
          with DataModule2.results do
          begin
            Close;
            Params[0].AsDateTime := Today;
            Params[1].AsString := a;
            ExecSQL;
          end;
        finally
          LeaveCriticalSection(LockDB);
        end;
      end;
    end;
    

    【讨论】:

    • pkt := IO.ReadLn(END_PACKET, Indy8BitEncoding) + END_PACKET; 它会从服务器读取所有口袋吗?我正在使用带有“indy10.1.5_d7”的 D7,我认为它没有 ReadLn(END_PACKET,Indy8BitEncoding),在这种情况下我该怎么办?只使用 IO.ReadLn 可以吗?
    • ReadLn() 读取直到指定的终止符到达,然后它返回终止符之前的所有内容并丢弃终止符本身。您的原始代码将终止符保留在您的AnsiString 中,因此在我的示例中是+ END_PACKET 部分。您是否真的需要保留终结器取决于您。至于 Indy 10.1.5,它还不支持 Unicode,所以只需省略 Indy8BitEncoding 部分,但仍将 END_PACKET 终止符传递给 ReadLn()。或者升级到最新的 10.6.0 版本(你应该考虑做任何事情,因为 10.1.5 已经很老了)。
    • 程序永远不会通过这行代码:pkt := IO.ReadLn(END_PACKET) + END_PACKET; 好像END_PACKET 永远不会出现。
    • 如果ReadLn() 永远不会退出,那么要么永远不会接收到终结符(使用数据包嗅探器,例如 WireShark,来检查),要么你有另一个线程从同一个 IOHandler 中读取同时吃掉ReadLn()正在等待的终结者。
    • 我在看最新的 Indy,我可以看到你是最新的 Indy 的作者!非常尊重你雷米!
    猜你喜欢
    • 2021-12-12
    • 2014-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-19
    • 2014-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多