6

たくさん検索した結果、私が取り組んでいるインスタントメッセンジャーサーバーでは、IndyTCPサーバーを使用するのが最適だと思いました。私が今直面している唯一の問題は、接続されている他のクライアントにメッセージをブロードキャストして転送することです。同じクライアントに応答を送り返すことは問題ないようで、他のクライアントのアクティビティを停止しませんが、他のクライアントにメッセージを転送する場合、私が知っているメカニズムを使用しaContext.locklist、接続リスト間を繰り返して、データを受信するクライアント接続を見つけます。

ここでの問題は、リストがフリーズし、unlocklistが呼び出されるまで他のクライアントの要求を処理しないことだと思います。では、サーバーのパフォーマンスを損なうことはありませんか?リストをロックし、各メッセージを転送するために接続間を繰り返します(これはメッセンジャーで非常に頻繁に発生するためです)。これを行うためのより良い方法はありますか?

Indy10とDelphi7を使用しています

放送用コード:

Var tmpList: TList;
    i: Integer;
Begin
tmpList := IdServer.Contexts.LockList;

For i := 0 to tmpList.Count Do Begin
  TIdContext(tmpList[i]).Connection.Socket.WriteLn('Broadcast message');
End;
IdServer.Contexts.UnlockList;

メッセージを転送するためのコード:

Var tmpList: TList;
  i: Integer;
Begin
  tmpList := IdServer.Contexts.LockList;

  For i := 0 to tmpList.Count Do Begin
    If TIdContext(tmpList[i]).Connection.Socket.Tag = idReceiver Then
      TIdContext(tmpList[i]).Connection.Socket.WriteLn('Message');
  End;
  IdServer.Contexts.UnlockList;
4

1 に答える 1

12

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;
于 2012-12-31T21:07:45.670 に答える