Indy 10 + Delphi Client-Server App Eats up all CPU

你。 提交于 2019-12-06 17:49:29

The reason your CPU uage is pegged is because your OnExecute event handler is not actually doing anything, so each connection thread is effectively running a tight loop that does not yield CPU timeslices to other threads that are waiting for CPU time. You need to have a yielding operation in that event handler. Once you implement your actual commands, that yielding will be handled by ReadLn() for you, but until you implement that then you can use a call to IndySleep() instead, eg:

procedure TForm1.TCPServerExecute(AContext: TIdContext); 
var 
  Client : PClient; 
  Command : string; 
  //PicturePathName : string; 
  ftmpStream : TFileStream; 
begin 
  Client := PClient(AContext.Data); 
  Client.LastAction := Now; 

  //Command := AContext.Connection.ReadLn; 
  IndySleep(10);
  //...
end; 

Now, with that said, there are some other issues in your code, such as misuse of TIdSocketHandles, thread safety issues, etc. Try this instead:

uses
  ..., IdContext, IdSync;

//...

type 
  PClient   = ^TClient; 
  TClient   = record 
    PeerIP      : String;            { Client IP address } 
    HostName    : String;            { Hostname } 
    Connected   : TDateTime;         { Time of connect } 
    LastAction  : TDateTime;         { Time of last transaction } 
    AContext    : TIdContext;        { Pointer to thread } 
  end; 

//...

procedure TForm1.StartServerExecute(Sender: TObject); 
begin 
  //setup and start TCPServer 
  TCPServer.Bindings.Clear;
  with TCPServer.Bindings.Add do 
  begin 
    IP := DefaultServerIP; 
    Port := DefaultServerPort; 
  end; 
  TCPServer.Active := True; 
  //setup TCPServer 

  //other startup settings 
  Protocol.Items.Add(TimeToStr(Time) + ' Shutdown server running on ' + TCPServer.Bindings[0].IP + ':' + IntToStr(TCPServer.Bindings[0].Port)); 
  RefreshListDisplay; 
end; 

procedue TForm1.RefreshListDisplay;
var
  List: TList;
  I: Integer;
  Client: PClient;
begin
  // clear display list as needed...
  List := TCPServer.Contexts.LockList;
  try
    for I := 0 to List.Count-1 do
    begin
      Client := PClient(TIdContext(List[I]).Data);
      if Client <> nil then
      begin
        // add Client to display list as needed..
      end;
    end;
  finally
    TCPServer.Contexts.UnlockList;
  end;
end;

type
  TProtocolNotify = class(TIdNotify)
  protected
    FStr: String;
    procedure DoNotify; override;
  public
    class procedure Add(const AStr: String);
  end;

procedure TProtocolNotify.DoNotify;
begin
  Form1.Protocol.Items.Add(FStr);
end;

class procedure TProtocolNotify.Add(const AStr: String);
begin
  with Create do
  begin
    FStr := AStr;
    Notify;
  end;
end;

type
  TRefreshListNotify = class(TIdNotify)
  protected
    procedure DoNotify; override;
  public
    class procedure Refresh;
  end;

procedure TRefreshListNotify.DoNotify;
begin
  Form1.RefreshListDisplay;
end;

class procedure TRefreshListNotify.Refresh;
begin
  Create.Notify;
end;

procedure TForm1.TCPServerConnect(AContext: TIdContext); 
var 
  NewClient: PClient; 
begin 
  GetMem(NewClient, SizeOf(TClient)); 
  try
    NewClient.PeerIP      := AContext.Connection.Socket.Binding.PeerIP; 
    NewClient.HostName    := GStack.HostByAddress(NewClient.PeerIP); 
    NewClient.Connected   := Now; 
    NewClient.LastAction  := NewClient.Connected; 
    NewClient.AContext    := AContext; 
    AContext.Data         := TObject(NewClient); 
  except
    FreeMem(NewClient);
    raise;
  end;

  TProtocolNotify.Add(TimeToStr(Time) + ' Connection from "' + NewClient.HostName + '" from ' + NewClient.PeerIP); 
  TRefreshListNotify.Refresh;
end; 

procedure TForm1.TCPServerDisconnect(AContext: TIdContext); 
var 
  Client: PClient; 
begin 
  Client := PClient(AContext.Data); 
  TProtocolNotify.Add(TimeToStr(Time) + ' Client "' + Client.HostName+'"' + ' disconnected.'); 
  FreeMem(Client); 
  AContext.Data := nil; 
  TRefreshListNotify.Refresh; 
end; 

procedure TForm1.TCPServerExecute(AContext: TIdContext); 
var 
  Client : PClient; 
  Command : string; 
  //PicturePathName : string; 
  ftmpStream : TFileStream; 
begin 
  Client := PClient(AContext.Data); 
  Client.LastAction := Now; 

  //Command := AContext.Connection.ReadLn; 
  IndySleep(10);

  if Command = 'CheckMe' then 
  begin 
    {do whatever necessary in here} 
  end; 
end; 

In TCPServerExecute(), you're not initializing Command.

You shouldn't be freeing Bindings in StartServerExecute(). Instead try something like this:

var
  sh: TidSocketHandle;
begin
  sh := TCPServer.Bindings.Add;
  sh.IP := DefaultServerIP;
  sh.Port := DefaultServerPort;

What is StartServerExecute()?

Unfortunately, there are too many problems with the code, and too much code missing to guess what's going on.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!