gpt4 book ai didi

delphi - Indy 10 + Delphi 客户端-服务器应用程序占用所有 CPU

转载 作者:行者123 更新时间:2023-12-03 05:28:18 27 4
gpt4 key购买 nike

我编写了一个小型客户端-服务器应用程序,该应用程序在两台或多台不同的计算机上运行,​​用于重新启动/关闭目的。由于我对客户端服务器应用程序相对较新,因此我选择了 About Delphi approach here 。简而言之,我的服务器应用程序等待端口 7676 上的连接,将客户端添加到客户端列表中,然后不执行任何操作(稍后将实现关闭和重新启动过程)。然而,即使它是被动的,它也会在仅连接两个客户端的情况下占用高达 90% 的 CPU。这是客户端代码,由 TidTCPServer 和 TidAntiFreeze 组成:

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

[...]

procedure TForm1.StartServerExecute(Sender: TObject);
var
Bindings: TIdSocketHandles;
begin

//setup and start TCPServer
Bindings := TIdSocketHandles.Create(TCPServer);
try
with Bindings.Add do
begin
IP := DefaultServerIP;
Port := DefaultServerPort;
end;
try
TCPServer.Bindings:=Bindings;
TCPServer.Active:=True;
except on E:Exception do
ShowMessage(E.Message);
end;
finally
Bindings.Free;
end;
//setup TCPServer

//other startup settings
Clients := TThreadList.Create;
Clients.Duplicates := dupAccept;

RefreshListDisplay;

if TCPServer.Active then
begin
Protocol.Items.Add(TimeToStr(Time)+' Shutdown server running on ' + TCPServer.Bindings[0].IP + ':' + IntToStr(TCPServer.Bindings[0].Port));
end;
end;

procedure TForm1.TCPServerConnect(AContext: TIdContext);
var
NewClient: PClient;
begin
GetMem(NewClient, SizeOf(TClient));

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);

try
Clients.LockList.Add(NewClient);
finally
Clients.UnlockList;
end;

Protocol.Items.Add(TimeToStr(Time)+' Connection from "' + NewClient.HostName + '" from ' + NewClient.PeerIP);
RefreshListDisplay;
end;

procedure TForm1.TCPServerDisconnect(AContext: TIdContext);
var
Client: PClient;
begin
Client := PClient(AContext.Data);
Protocol.Items.Add (TimeToStr(Time)+' Client "' + Client.HostName+'"' + ' disconnected.');
try
Clients.LockList.Remove(Client);
finally
Clients.UnlockList;
end;
FreeMem(Client);
AContext.Data := nil;

RefreshListDisplay;

end;

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

//Command := AContext.Connection.ReadLn;
if Command = 'CheckMe' then
begin
{do whatever necessary in here}
end;
end;
end;

idTCPServer组件设置如下:ListenQueue := 15, MaxConnections := 0, TerminateWaitTime: 5000。

我在这里做错了什么吗?我是否应该采取不同的方法来同时支持大约 30 - 40 个客户?

谢谢,鲍勃。

最佳答案

您的 CPU 使用率被固定的原因是您的 OnExecute 事件处理程序实际上没有执行任何操作,因此每个连接线程实际上都在运行一个紧密循环,不会为其他线程产生 CPU 时间片。等待CPU时间。您需要在该事件处理程序中进行让步操作。一旦您实现了实际的命令,该生成将由 ReadLn() 为您处理,但在您实现该命令之前,您可以使用对 IndySleep() 的调用来代替,例如:

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;

现在,话虽如此,您的代码中还存在一些其他问题,例如误用 TIdSocketHandles、线程安全问题等。请尝试以下操作:

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;

关于delphi - Indy 10 + Delphi 客户端-服务器应用程序占用所有 CPU,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12164888/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com