繁体   English   中英

为什么我的Delphi Indy idHTTpServer在CLOSE_WAIT时停止响应?

[英]Why does my Delphi Indy idHTTpServer stop responding when CLOSE_WAIT?

环境

我已经使用Indy组件TidHTTPServer在Delphi中创建了一个Web服务器。 我正在使用Indy版本10.5.8附带的Delphi XE2。 该服务器正在作为一个桌面应用程序运行,其形式为显示连接及其请求的日志。 它在Windows 7 Professional上运行。 请求是针对来自Firebird数据库的SQL数据。 响应为JSON。 所有流量均为HTTP。

挑战

当我与少量用户进行测试时,一切工作正常。 现在,我已将其推广到约400个用户,出现了通信问题。 服务器停止响应请求,而我可以使其再次响应的唯一方法是重新启动运行它的计算机,然后重新启动它。 在高容量时间内,重新引导的需求更加频繁。

病征

使用Windows netstat时,我注意到每当发生CLOSE_WAIT类型的TCP连接时,服务器就会停止响应请求,而我必须再次重新启动

测试程序

即使服务器上没有流量,我也能够模拟这种挂起。 我创建了一个网页,该网页发送多个请求,每个请求之间都有延迟。

该网页让我指定要发出的请求数,每个请求之间要等待多长时间以及超时之前要等待多长时间。 甚至在两次请求之间的一毫秒之间,服务器似乎也没有问题。

测试结果

如果将每个请求的超时时间设置为一个很小的数字(例如1毫秒),则可以使Delphi HTTP Server挂起。 正如我所期望的,在1毫秒的超时时间内,对我的服务器的请求每次都会失败。 超时太短了,我的服务器可能无法足够快地响应。

我不明白的是,在客户端强制超时后,即使请求数量相对较少(少于50个),Delphi Web服务器也不再响应任何请求。 当我在服务器计算机上运行netstat时,有许多CLOSE_WAIT套接字连接。 即使经过一个小时,并且在关闭服务器后,CLOSE_WAIT套接字连接仍然存在。

问题

到底是怎么回事? 当有(甚至只有一个)CLOSE_WAIT套接字连接时,为什么我的Delphi Indy idHTTPServer停止响应? CLOSE_WAIT不会消失,服务器也不会再次开始响应。 我必须重启。

我在做什么

这是netstat命令显示CLOSE_WAIT的结果:

C:\Windows\system32>netstat -abn | findstr 62000
TCP    0.0.0.0:62000          0.0.0.0:0             LISTENING
TCP    10.1.1.13:62000        9.49.1.3:57036        TIME_WAIT
TCP    10.1.1.13:62000        9.49.1.3:57162        CLOSE_WAIT
TCP    10.1.1.13:62000        9.49.1.3:57215        CLOSE_WAIT
TCP    10.1.1.13:62000        9.49.1.3:57244        CLOSE_WAIT
TCP    10.1.1.13:62000        9.49.1.3:57263        CLOSE_WAIT
TCP    10.1.1.13:62000        9.49.1.3:57279        ESTABLISHED
TCP    10.1.1.13:62000        104.236.216.73:59051  ESTABLISHED

这是我的Web服务器的本质:

unit MyWebServer;

interface

Uses
...

Type
  TfrmWebServer = class(TForm)
    ...
    IdHTTPServer: TIdHTTPServer;
    ...
    procedure IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    procedure IdHTTPServerDisconnect(AContext: TIdContext);
    procedure btnStartClick(Sender: TObject);
    ...  
    dbFirebird : TIBDatabase;
    txFireird  : TIBTransaction;
    ...
  private
    function CreateSomeResponseStringData: string;
  end;


implementation

procedure TfrmWebServer.btnStartClick(Sender: TObject);
  begin
    {set the IP's and proit to listen on}
    IdHTTPServer.Bindings.Clear;
    IdHTTPServer.Bindings.Add.IP   := GetSetting(OPTION_TCPIP_ADDRESS);
    IdHTTPServer.Bindings.Add.Port := Str2Int(GetSetting(OPTION_TCPIP_PORT));
    {start the web server}
    IdHTTPServer.Active := TRUE;
    ...
    dbFirebird.Transactrion := txFirebird;
    ...
  end;

procedure TfrmWebServer.IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  var
    qryFirebird : TIBSql;

  function CreateSomeResponseStringData: string;
    begin
      qryFirebird := NIL;
      qryFirebird := TIBSql.Create(IdHTTPServer);
      qryFirebird.Database := dbFirebird;
      dbFirebird.Connected := FALSE;
      dbFirebird.Connected := TRUE;
      qryFirebird.Active := TRUE;
      Result := {...whatever string will be returned}
    end;

  function CreateAnErrorResponse: string;
    begin
      Result := {...whatever string will be returned}
    end;

  begin
    try        
      AResponseInfo.ContentText := CreateSomeResponseStringData;
      {Clean up: What do I do here to make sure that the connection that was served is:
         - properly closed so that I don't run out of resourses?
         - anything that needs to be cleaned up is freed so no memory leaks
         - TIME_WAIT, CLOSE_WAIT, any other kind of _WAITs are not accumulating?}
    except;
      AResponseInfo.ContentText := CreateAnErrorResponse;
    end;
    qryFirebird.Free;
  end;

procedure TfrmWebServer.IdHTTPServerDisconnect(AContext: TIdContext);
  begin
    {Maybe I do the "Clean Up" here? I tried Disconnect as shown but still lots of 
    TIME_WAIT tcp/ip connections accumulate. even after the app is closed}    
    AContext.Connection.Disconnect;
  end;

end.  

此代码至少存在两个可能导致崩溃的主要问题:

  1. 数据库和事务对象是IdHTTPServer创建的所有线程的全局对象。 当您断开数据库连接时,它将断开所有线程的连接。

  2. 如果在分配内容文本时发生运行时错误,则此行AResponseInfo.ContentText := CreateAnErrorResponse; 不在异常块中。

这是我要解决的方法:

...
procedure TfrmWebServer.btnStartClick(Sender: TObject);
  begin
    {set the IP's and port to listen on}
    IdHTTPServer.Bindings.Clear;
    IdHTTPServer.Default.Port    := Str2Int(GetSetting(OPTION_TCPIP_PORT));
    IdHTTPServer.Bindings.Add.IP := GetSetting(OPTION_TCPIP_ADDRESS);
    {start the web server}
    IdHTTPServer.Active := TRUE;
    ...
  end;

procedure TfrmWebServer.IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  var
    {make these local to each thread}
    qryFirebird : TIBSql;
    dbFirebird  : TIBDatabase;
    txFirebird  : TIBTransaction;

  function CreateSomeResponseStringData: string;
    begin
      dbFirebird  := TIBDatbase.Create(IdHTTPServer);
      txFirebird  := TIBTransaction.Create(IdHTTPServer);
      qryFirebird := TIBSql.Create(IdHTTPServer);
      dbFirebird.Transaction := txFirebird;
      qryFirebird.Database := dbFirebird;
      ...Add params that do the log in to database
      dbFirebird.Connected := TRUE;
      qryFirebird.Active := TRUE;
      Result := {...whatever string will be returned}
    end;

  function CreateAnErrorResponse: string;
    begin
      Result := {...whatever string will be returned}
    end;

  begin
    try
      try        
        ...
        AResponseInfo.ContentText := CreateSomeResponseStringData;
        ...
      except;
        try
          AResponseInfo.ContentText := CreateAnErrorResponse;
        except
          {give up}
        end;
      end;
    finaly
      qryFirebird.Free;
      dbFirebird.Free;
      txFirebird.Free;
    end;
  end;

end.     

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM