根据 Remy Lebeau 的几个问题和几乎满足的回答(再次感谢您),我尝试结合对我的应用程序有用的代码。有几个方面让我不清楚。当您查看下面的代码时:
- 当我使用 Button3Click 程序将广播从 GUI 发送到连接的客户端时 - 这是正确的方法(我的意思是:它安全吗)?
- 我可以放入类似于 DoSomethingSafe 的方法代码,在其中创建与 DB 的连接、在其上执行某些操作并关闭与 DB 的连接吗?安全吗?
- 为什么我的应用程序在超过 20 个客户端时冻结,并且我想通过使用它的 active:=false(button2.click 方法)来停止工作服务器?
- 我可以在 TCliContext.ProccessMsg 中使用 TCliContext.BroadcastMessage 来调用它而不进行任何同步吗?
- 我可以在 OnConnect 方法中读取 (Connection.IOHandler.ReadLn()) 吗(我想读取登录数据行并在 DB 中检查它,然后当它不正确时立即断开连接?
- 我在某处读到,使用 IdSync 有时是危险的(如果使用它内部出现任何问题),因此我有最后一个问题:什么是访问全局变量或 VCL 对象的更好解决方案?
我的示例代码如下所示:
type
TCliContext = class(TIdServerContext)
private
Who: String;
Queue: TIdThreadSafeStringList;
Activity_time: TDateTime;
Heartbeat_time: TDateTime;
InnerMessage: String;
procedure BroadcastMessage(const ABuffer: String);
procedure SendMessageTo(const ADestUser: String; const ABuffer: String);
public
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
destructor Destroy; override;
procedure ProccessMsg;
procedure DoSomethingSafe;
procedure info_about_start_connection;
end;
procedure TCliContext.BroadcastMessage(const ABuffer: String);
var
cList: TList;
Count: Integer;
CliContext: TCliContext;
begin
cList := Server.Contexts.LockList;
try
for Count := 0 to cList.Count - 1 do
begin
CliContext := TCliContext(cList[Count]);
if CliContext <> Self then
CliContext.Queue.Add(ABuffer);
end;
finally
Server.Contexts.UnlockList;
end;
end;
procedure TCliContext.SendMessageTo(const ADestUser: String;
const ABuffer: String);
var
cList: TList;
Count: Integer;
CliContext: TCliContext;
begin
cList := Server.Contexts.LockList;
try
for Count := 0 to cList.Count - 1 do
begin
CliContext := TCliContext(cList[Count]);
if CliContext.Who = ADestUser then
begin
CliContext.Queue.Add(ABuffer);
Break;
end;
end;
finally
Server.Contexts.UnlockList;
end;
end;
constructor TCliContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil);
begin
// inherited Create(AConnection, AYarn, AList);
inherited;
Queue := TIdThreadSafeStringList.Create;
end;
destructor TCliContext.Destroy;
begin
Queue.Free;
inherited;
end;
procedure TCliContext.ProccessMsg;
begin
InnerMessage := Connection.IOHandler.ReadLn();
TIdSync.SynchronizeMethod(DoSomethingSafe);
// is it ok?
end;
procedure TCliContext.info_about_start_connection;
begin
MainForm.Memo1.Lines.Add('connected');
end;
procedure TCliContext.DoSomethingSafe;
begin
MainForm.Memo1.Lines.Add(InnerMessage);
end;
和代码与 GUI 相关联
procedure TMainForm.BroadcastMessage(Message: string);
var
cList: TList;
Count: Integer;
begin
cList := IdTCPServer.Contexts.LockList;
try
for Count := 0 to cList.Count - 1 do
TCliContext(cList[Count]).Queue.Add(Message);
finally
IdTCPServer.Contexts.UnlockList;
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
IdTCPServer.ContextClass := TCliContext;
end;
procedure TMainForm.IdTCPServerConnect(AContext: TIdContext);
begin
TCliContext(AContext).Queue.Clear;
TCliContext(AContext).Heartbeat_time := now;
TCliContext(AContext).Activity_time := now;
TIdSync.SynchronizeMethod(TCliContext(AContext).info_about_start_connection);
// is it safe?
end;
procedure TMainForm.IdTCPServerExecute(AContext: TIdContext);
var
tmplist, Queue: TStringlist;
dtNow: TDateTime;
begin
dtNow := now;
tmplist := nil;
try
Queue := TCliContext(AContext).Queue.Lock;
try
if Queue.Count > 0 then
begin
tmplist := TStringlist.Create;
tmplist.Assign(Queue);
Queue.Clear;
end;
finally
TCliContext(AContext).Queue.Unlock;
end;
if tmplist <> nil then
begin
AContext.Connection.IOHandler.Write(tmplist);
TCliContext(AContext).Heartbeat_time := dtNow;
end;
finally
tmplist.Free;
end;
if SecondsBetween(dtNow, TCliContext(AContext).Heartbeat_time) > 30 then
begin
AContext.Connection.IOHandler.WriteLn('E:');
TCliContext(AContext).Heartbeat_time := dtNow;
end;
if SecondsBetween(dtNow, TCliContext(AContext).Activity_time) > 6 then
begin
AContext.Connection.Disconnect;
Exit;
end;
TCliContext(AContext).ProccessMsg;;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
IdTCPServer.Active := true;
end;
procedure TMainForm.Button2Click(Sender: TObject);
begin
IdTCPServer.Active := false;
// here application freezes when there are more then tens active clients
end;
procedure TMainForm.Button3Click(Sender: TObject);
begin
BroadcastMessage('Hello');
// is it safe and correct?
end;
更新(您出色回答后的最后一个问题)要更简单(代码长度更短),我可以使用 TIdNotify 类,如下所示:
TMyNotify.Create(1, 'ABC').Notify;
.
type
TMyNotify = class(TidNotify)
public
faction: string;
fdata:string;
procedure DoNotify; override;
procedure action1();
procedure action2();
constructor Create(action:integer;fdata:string); reintroduce;
end;
constructor TMyNotify.Create(action:integer;fdata:string); reintroduce;
begin
inherited Create;
faction:=action;
fdata:=data;
end;
procedure TMyNotify.action2()
begin
//use fdata and do something with vcl etc.
end;
procedure TMyNotify.action2()
begin
//use fdata and do something with vcl etc.
end;
procedure TMyNotify.DoNotify;
begin
case action of
1: action1()
2: action2()
end;
end;
再次感谢您之前的帮助