印第10 TCP服务器(Indy 10 TCP server)

2019-07-17 17:59发布

很多搜索后,我认为印TCP服务器将是最好的,以我的工作即时通讯服务器上使用。 我面对现在唯一的问题是广播和转发消息给其他连接的客户端,发送回响应同一客户端似乎确定,不挂断其他客户活动,但对于转发消息给其他客户,我知道的机制是使用aContext.locklist ,和连接列表之间进行迭代,找到客户端连接要接收的数据。

在这里,我想的问题是,它冻结清单,并直至unlocklist被称为不处理其他客户机的请求。 所以,这不是要伤害服务器的性能? 锁定列表和连接之间的迭代转发每个消息(因为这是一个非常信使经常发生)。 有没有更好的方式来做到这一点?

我使用的印第10和Delphi 7

规范广播:

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;

Answer 1:

是的,你必须通过环Contexts列表,以广播消息给多个客户端。 但是,你不(也不应该)从循环中执行实际的写作。 一,因为你已经注意到了,服务器的性能可以通过保持锁定一段时间,该列表的影响。 二,它不是线程安全的。 如果你的循环,而另一个线程正在写在同一时间同一个连接将数据写入到一个连接,那么这两个写将互相重叠,并破坏你与客户端通信。

我通常做的是落实在每个客户端出站队列代替,即使用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;


文章来源: Indy 10 TCP server