我正在寻求帮助,发生了几天后我的tcpserver接受连接。或多或少总是在1400个连接中。 我使用一个类(TObject)来注册连接并在客户端断开连接时销毁,但显然总是卡住连接。
Type
TSimpleClient = class(TObject)
IP : ShortString;
Connection : TidTCPServerConnection;
Deteccao : ShortString;
Existe : ShortString;
Contagem : Integer;
Bloqueado : ShortString;
Serial : ShortString;
Thread : Pointer;
end;
没有onconnectfaçooseguinte:
Client := TSimpleClient.Create;
Client.IP := AThread.Connection.Socket.Binding.PeerIP;
Client.Thread := AThread;
Client.Serial := Received;
Client.Connection := AThread.Connection;
AThread.Data := Client;
没有断开连接:
Client := Pointer(AThread.Data);
IP := Client.IP;
Serial := Client.Serial;
try
Client.Free;
finally
AThread.Data := nil;
end;
我已经使用记录来存储和释放连接,但是一直存在连接卡住和服务器接收连接的问题。
更新:我的onxecute简单。像这样的东西:
Client := Pointer(AThread.Data);
try
Received := AThread.Connection.ReadLn;
Received := Protege('D', Received);
A := TStringList.Create;
Split('=', Received, A);
if A[0] = 'teste' then begin
//...
end;
A.Free;
except
on e: Exception do begin
AThread.Connection.Disconnect;
end;
end;
答案 0 :(得分:0)
Indy每个连接的客户端使用1个线程。在单个32位进程中,Windows将以大约1400-2000个并发线程达到最大2GB内存限制(请参阅Does Windows have a limit of 2000 threads per process)时启动无法创建新线程,具体取决于每个线程使用的默认堆栈大小。
如果您需要处理更多客户,您必须:
调整项目的设置以降低可执行文件的默认线程堆栈大小。默认值为1MB。
编写一个自定义TIdPeerThread
派生类,绕过TThread
构造函数,并使用自定义堆栈大小直接调用RTL的BeginThread()
函数。然后将TIdThreadMgrDefault
或TIdThreadMgrPool
组件分配给服务器的ThreadMgr
属性,然后将类类型分配给线程管理器的ThreadClass
属性。
使用某种负载平衡系统来处理多个服务器进程中的客户端。
以太方式,在处理那么多客户端时,您应该考虑从每个#2上面的TIdPeerThread
派生一个自定义类,这样您就可以将自定义字段放在该类中。这样,您不必使用TIdPeerThread.Data
属性来跟踪内存中的其他对象,这将节省一些空间。例如:
type
TSimpleClient = class(TIdPeerThread)
public
IP : ShortString;
Deteccao : ShortString;
Existe : ShortString;
Contagem : Integer;
Bloqueado : ShortString;
Serial : ShortString;
end;
procedure TMyForm.FormCreate(Sender: TObject);
begin
IdThreadMgrDefault1.ThreadClass := TSimpleClient;
end;
procedure TMyForm.IdTCPServer1Connect(AThread: TIdPeerThread);
var
Client: TSimpleClient;
Received: string;
begin
Client := TSimpleClient(AThread);
Client.IP := AThread.Connection.Socket.Binding.PeerIP;
Received := ...;
Client.Serial := Received;
//...
end;
procedure TMyForm.IdTCPServer1Disconnect(AThread: TIdPeerThread);
var
Client: TSimpleClient;
IP, Serial: string;
begin
Client := TSimpleClient(AThread);
IP := Client.IP;
Serial := Client.Serial;
//...
// no need to free the Client here...
end;
procedure TMyForm.IdTCPServer1Execute(AThread: TIdPeerThread);
var
Client: TSimpleClient;
Received: string;
begin
Client := TSimpleClient(AThread);
Received := AThread.Connection.ReadLn;
Received := Protege('D', Received);
A := TStringList.Create;
try
Split('=', Received, A);
if (A.Count > 0) and (A[0] = 'teste') then
begin
//...
end;
finally
A.Free;
end;
// no need to catch an exception here just to
// Disconnect, TIdTCPServer handles that for you...
end;
答案 1 :(得分:-1)
如果您确定在关闭连接时正确清理对象,则应查看setsockopt
和SO_REUSEADDR
。当连接关闭时,TCP子系统默认将地址和端口的组合保持在等待状态,只是为了捕获可能在线路上留下的任何数据。在断开连接之后。 (如果我记错了最多10分钟。)您可以使用netstat -a
命令行实用程序进行检查。
如果您的服务处理大量连接,它可能会在几千个连接后停止响应,因为所有传出TCP端口都以这种方式占用。
为了避免将SO_REUSEADDR
设置为setsockopt
为真,每次连接一次,系统允许重复使用地址和端口组合。像这样:
var
i:integer;
begin
i:=1;
setsockopt(Socket.Handle,SOL_SOCKET,SO_REUSEADDR,@i,4);