经过大量搜索后,我认为 Indy TCP 服务器最适合在我正在开发的即时通讯服务器上使用。我现在面临的唯一问题是向其他连接的客户端广播和转发消息,向同一客户端发回响应似乎没问题,并且不会挂起其他客户端活动,但对于将消息转发到其他客户端,我知道的机制是通过使用aContext.locklist
,并在连接列表之间迭代以查找要接收数据的客户端连接。
我认为这里的问题是它冻结列表并且在调用解锁列表之前不处理其他客户端请求。那么这样不会损害服务器的性能吗?锁定列表并在连接之间迭代以转发每条消息(因为这是在信使中经常发生的情况)。有没有更好的方法来做到这一点?
我正在使用 Indy 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;
是的,你必须循环遍历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;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)