【问题标题】:TcpServer Cant Accept More Connections After Some Days几天后,TcpServer 无法接受更多连接
【发布时间】:2016-04-13 17:53:34
【问题描述】:

我正在寻求帮助,几天后我的 tcpserver 接受了连接。或多或少总是在 1400 个连接中。 我使用一个类(TObject)来注册连接并在客户端断开连接但显然总是卡住连接时销毁。

Type 
TSimpleClient = class(TObject)
IP          : ShortString;
Connection  : TidTCPServerConnection;
Deteccao    : ShortString;
Existe      : ShortString;
Contagem    : Integer;
Bloqueado   : ShortString;
Serial      : ShortString;
Thread      : Pointer;
end;

没有onconnect faço o seguinte:

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;

我使用记录来存储和释放连接,但总是遇到连接卡住和服务器接收连接的问题。


更新:我的执行很简单。像这样:

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;

【问题讨论】:

    标签: delphi indy connection tcpserver


    【解决方案1】:

    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. 使用某种负载平衡系统来处理多个服务器进程之间的客户端。

    以另一种方式,在与这么多客户打交道时,您应该考虑根据上面的 #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;
    

    【讨论】:

    • 我认为问题在于帐户被抓住并最终导致了这个问题。但为什么会卡住我不知道。我使用此代码查看有多少已连接: function Online: Integer; var NumClients:整数;从 Form1.IdTCPServer1.Threads.LockList 开始尝试 NumClients := Count;最后是 Form1.IdTCPServer1.Threads.UnlockList;结尾;结果:= NumClients;结束;
    • 您尚未显示任何TIdTCPServer.OnExecute 代码,因此无法诊断该问题..
    【解决方案2】:

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

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

    为避免将SO_REUSEADDRsetsockopt 一起设置为true,在每个连接上一次,因此系统允许重复使用地址和端口组合。像这样:

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

    【讨论】:

    • @Remy Lebeau,我的执行很简单。像这样的东西:'Client := Pointer(AThread.Data);尝试收到:= AThread.Connection.ReadLn;收到 := Protege('D', 收到); A := TStringList.Create;拆分('=',收到,A);如果 A[0] = 'teste' then begin} end;免费; e除外:异常开始AThread.Connection.Disconnect;结尾;结尾;在连接和执行时,我访问另一个我的过程,该过程在运行时创建 TSqlConnection 和 TSqlDataSet 以插入和检查信息,然后使用并免费。是错误吗?我认为没有。
    • @DouglasRuiz,请编辑您的问题并将代码放在那里,而不是在评论中...
    • @Remy Lebeau,使用您的代码我无法多次连接,如果我断开连接,我在 onexecute 时会出错(指针无效)。
    • @DouglasRuiz:那你做错了什么。请编辑您的问题以显示您的最新代码。
    • @RemyLebeau 使用您的代码我只能连接一次,如果我断开连接并再次尝试连接,服务器不会执行任何操作:/
    猜你喜欢
    • 2015-08-28
    • 1970-01-01
    • 2015-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    • 1970-01-01
    相关资源
    最近更新 更多