Contexts
はい、メッセージを複数のクライアントにブロードキャストするには、リストをループする必要があります。ただし、ループ内から実際の書き込みを実行することはありません (実行すべきではありません)。1 つは、既にお気づきのように、リストをしばらくロックしておくと、サーバーのパフォーマンスに影響を与える可能性があることです。2 つ目は、スレッドセーフではありません。別のスレッドが同時に同じ接続に書き込みを行っているときに、ループが接続にデータを書き込むと、2 つの書き込みが互いにオーバーラップし、そのクライアントとの通信が破損します。
私が通常行っているのは、実際のキューを保持するために TIdContext.Data プロパティまたは TIdServerContext の子孫を使用して、代わりにクライアントごとのアウトバウンド キューを実装することです。クライアントのイベントの外部からクライアントにデータを送信する必要がある場合はOnExecute
、代わりにそのクライアントのキューにデータを入れます。そのクライアントのOnExecute
イベントは、安全に送信できるようになったときに、キューの内容をクライアントに送信できます。
例えば:
type
TMyContext = class(TIdServerContext)
public
Tag: Integer;
Queue: TIdThreadSafeStringList;
...
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
destructor Destroy; override;
end;
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil);
begin
inherited;
Queue := TIdThreadSafeStringList.Create;
end;
destructor TMyContext.Destroy;
begin
Queue.Free;
inherited;
end;
.
procedure TForm1.FormCreate(Sender: TObject);
begin
IdServer.ContextClass := TMyContext;
end;
procedure TForm1.IdServerConnect(AContext: TIdContext);
begin
TMyContext(AContext).Queue.Clear;
TMyContext(AContext).Tag := ...
end;
procedure TForm1.IdServerDisconnect(AContext: TIdContext);
begin
TMyContext(AContext).Queue.Clear;
end;
procedure TForm1.IdServerExecute(AContext: TIdContext);
var
Queue: TStringList;
tmpList: TStringList;
begin
...
tmpList := nil;
try
Queue := TMyContext(AContext).Queue.Lock;
try
if Queue.Count > 0 then
begin
tmpList := TStringList.Create;
tmpList.Assign(Queue);
Queue.Clear;
end;
finally
TMyContext(AContext).Queue.Unlock;
end;
if tmpList <> nil then
AContext.Connection.IOHandler.Write(tmpList);
finally
tmpList.Free;
end;
...
end;
.
var
tmpList: TList;
i: Integer;
begin
tmpList := IdServer.Contexts.LockList;
try
for i := 0 to tmpList.Count-1 do
TMyContext(tmpList[i]).Queue.Add('Broadcast message');
finally
IdServer.Contexts.UnlockList;
end;
end;
.
var
tmpList: TList;
i: Integer;
begin
tmpList := IdServer.Contexts.LockList;
try
for i := 0 to tmpList.Count-1 do
begin
if TMyContext(tmpList[i]).Tag = idReceiver then
TMyContext(tmpList[i]).Queue.Add('Message');
end;
finally
IdServer.Contexts.UnlockList;
end;
end;