【发布时间】:2015-02-19 07:54:56
【问题描述】:
我有以下代码,“改编”自 Lebeau 在另一篇文章中的回答:Delphi XE2 / Indy TIdTCPServer / "Connection reset by peer"
type
TClient = class(TObject)
public
Host: String;
Queue: TIdThreadSafeStringList;
end;
var
Clients: TThreadList;
function TMain.HostOnTList(const Host: String): Pointer;
var
I: Integer;
List: TList;
begin
Result := nil;
List := Clients.LockList;
try
for I := 0 to List.Count - 1 do
if (TClient(List[I]).Host = Host) then
begin
Result := List[I];
Break;
end;
finally
Clients.UnlockList;
end;
end;
procedure TMain.FormCreate(Sender: TObject);
const
Hosts: Array[0..4] of String = (
'HOST1', 'HOST2', 'HOST3', 'HOST4, 'HOST5'
);
var
I: Integer;
List: TList;
Client: TClient;
begin
Clients := TThreadList.Create;
Clients.Duplicates := dupAccept;
for I := Low(Hosts) to High(Hosts) do
begin
Client := TClient.Create;
Client.Host := Hosts[I];
Client.Queue := TIdThreadSafeStringList.Create;
Clients.Add(Client);
Client := nil;
end;
end;
procedure TMain.FormDestroy(Sender: TObject);
var
I: Integer;
List: TList;
begin
if TCPServer.Active Then
TCPServer.Active := False;
List := Clients.LockList;
try
for I := 0 to List.Count - 1 do
TClient(List[I]).Free;
finally
Clients.UnlockList;
Clients.Free;
end;
end;
procedure TMain.TCPServerConnect(AContext: TIdContext);
var
Host: String; // Host String
CIdx: Pointer; // Client Pointer
begin
... (get context hostname)
CIdx := HostOnTList(Host);
if (CIdx <> nil) then
AContext.Data := TClient(CIdx);
else
... (disconnect client)
end;
procedure TMain.TCPServerDisconnect(AContext: TIdContext);
var
List: TList;
Host: String;
Client: TClient;
begin
Host := '';
Client := TClient(AContext.Data);
List := Clients.LockList;
try
Host := Client.Host;
if (Host <> '') then
begin
Client.Queue := nil;
AContext.Data := nil;
end;
finally
Clients.UnlockList;
end;
end;
procedure TMain.idTCPServerExecute(AContext: TIdContext);
var
I: Integer;
List: TStringList;
begin
Client := TClient(AContext.Data);
...
List := Client.Queue.Lock;
try
while List.Count > 0 do
begin
WriteLn(List[0]);
List.Delete(0);
end;
finally
Client.Queue.Unlock;
end;
...
end;
function TMain.SendMessage(const Host, Msg: String): Boolean;
var
List: TList;
CIdx: Pointer;
begin
Result := False;
CIdx := HostOnTList(Host);
if (CIdx <> nil) then
begin
List := TCPServer.Contexts.LockList;
try
TClient(CIdx).Queue.Add(Msg);
Result := True;
finally
TCPServer.Contexts.UnlockList;
end;
end;
end;
但是发生了一个奇怪的行为......客户端可以连接,但是一旦断开并尝试再次连接,它就被断开了。
我试图注释代码行直到找到问题所在,它发生在这一行:“List := Client.Queue.Lock;”在 idTCPServerExecute 过程中。
请问有谁知道怎么回事?
谢谢!
【问题讨论】:
-
您的
OnConnect处理程序是否分配AContext.Data := Client;?您没有显示这一点,但您的OnExecute处理程序正在期待它(我在链接讨论中提供的代码完成了该任务)。你有一个OnDisconnect处理程序来释放TClient和Queue对象吗?话虽如此,我建议从TIdServerContext派生TClient并将其分配给服务器的ContextClass属性,并完全摆脱Clients列表。服务器有自己的公共Contexts列表,用于跟踪连接的客户端。 -
我看到您仍在使用自己的
Clients列表,而不是像我在您链接到的其他讨论中建议的那样消除它。我强烈建议你删除自己的列表,因为你只是在复制TIdTCPServer已经为你做的事情。 -
@RemyLebeau,实际上我的代码有些不同,因为我需要一个填充的 tclients 列表,并更改它们的状态 OnConnect 和 OnDisconnect 事件。像这样stackoverflow.com/questions/27555342/…。但我在连接时分配 AContext.Data := Client ,在断开连接时分配 AContext.Data := nil 。我将更新我的问题中的代码。谢谢!
标签: multithreading delphi tcp indy