几天后TcpServer无法接受更多连接

时间:2016-04-13 17:53:34

标签: delphi indy connection tcpserver

我正在寻求帮助,发生了几天后我的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;

2 个答案:

答案 0 :(得分:0)

Indy每个连接的客户端使用1个线程。在单个32位进程中,Windows将以大约1400-2000个并发线程达到最大2GB内存限制(请参阅Does Windows have a limit of 2000 threads per process)时启动无法创建新线程,具体取决于每个线程使用的默认堆栈大小。

如果您需要处理更多客户,您必须:

  1. 调整项目的设置以降低可执行文件的默认线程堆栈大小。默认值为1MB。

  2. 编写一个自定义TIdPeerThread派生类,绕过TThread构造函数,并使用自定义堆栈大小直接调用RTL的BeginThread()函数。然后将TIdThreadMgrDefaultTIdThreadMgrPool组件分配给服务器的ThreadMgr属性,然后将类类型分配给线程管理器的ThreadClass属性。

  3. 使用某种负载平衡系统来处理多个服务器进程中的客户端。

  4. 以太方式,在处理那么多客户端时,您应该考虑从每个#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)

如果您确定在关闭连接时正确清理对象,则应查看setsockoptSO_REUSEADDR。当连接关闭时,TCP子系统默认将地址和端口的组合保持在等待状态,只是为了捕获可能在线路上留下的任何数据。在断开连接之后。 (如果我记错了最多10分钟。)您可以使用netstat -a命令行实用程序进行检查。

如果您的服务处理大量连接,它可能会在几千个连接后停止响应,因为所有传出TCP端口都以这种方式占用。

为了避免将SO_REUSEADDR设置为setsockopt为真,每次连接一次,系统允许重复使用地址和端口组合。像这样:

var
  i:integer;
begin
  i:=1;
  setsockopt(Socket.Handle,SOL_SOCKET,SO_REUSEADDR,@i,4);