为什么我的 Delphi Indy idHTTPServer 在 CLOSE_WAIT 时停止响应?
Why does my Delphi Indy idHTTpServer stop responding when CLOSE_WAIT?
环境
我在 Delphi 中使用 Indy 组件 TidHTTPServer 创建了一个网络服务器。我正在使用 Indy 版本 10.5.8 附带的 Delphi XE2。这
服务器 运行 正在作为桌面应用程序使用一个显示连接及其请求日志的表单。 运行宁 Windows 7
专业的。请求来自 Firebird 数据库的 SQL 数据。响应是 JSON。所有流量都是 HTTP。
挑战
当我对少量用户进行测试时,一切都运行良好。现在我已经将它推广到大约 400 个用户
沟通问题。服务器停止响应请求,我可以让它再次响应的唯一方法是重新启动它正在 运行ning 的机器,然后重新启动它。期间需要更频繁地重新启动
高音量时间。
症状
使用 Windows netstat 我注意到每当出现 CLOSE_WAIT 类型的 TCP 连接时,服务器就会停止响应请求,我必须重新启动
测试程序
即使服务器上没有流量,我也能够模拟这种挂起。我创建了一个发送多个请求的网页
每个请求之间的延迟。
网页让我指定要发出的请求数、每个请求之间等待多长时间以及超时前等待多长时间。即使在请求之间只有一毫秒的时间,服务器似乎也可以毫无问题地做出响应。
测试结果
如果我将每个请求的超时时间设置为一个非常小的数字,比如 1 毫秒,我可以让我的 Delphi HTTP 服务器挂起。正如我所料,在 1 毫秒超时时,对我的服务器的请求每次都会失败。超时时间太短,我的服务器可能无法足够快地响应。
我不明白的是,在我在客户端强制超时后,即使请求数量相对较少(少于 50 个),我的 Delphi 网络服务器也不再响应任何请求.当我在服务器机器上 运行 netstat 时,有许多 CLOSE_WAIT 套接字连接。即使在一个小时后并关闭我的服务器后,CLOSE_WAIT 套接字连接仍然存在。
问题
这是怎么回事?为什么我的 Delphi Indy idHTTPServer 在有(即使只有一个)CLOSE_WAIT 套接字连接时停止响应? CLOSE_WAITs 不会消失,服务器不会再次开始响应。我必须重新启动。
我没有做什么?
这是显示 CLOSE_WAITs 的 netstat 命令的结果:
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
这是我的网络服务器的精华:
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.
此代码至少存在两个可能导致崩溃的主要问题:
数据库和事务对象对于 IdHTTPServer
创建的所有线程都是全局的。当您断开数据库连接时,它会断开所有线程。
如果在分配内容文本时出现 运行 时间错误,此行 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.
环境
我在 Delphi 中使用 Indy 组件 TidHTTPServer 创建了一个网络服务器。我正在使用 Indy 版本 10.5.8 附带的 Delphi XE2。这 服务器 运行 正在作为桌面应用程序使用一个显示连接及其请求日志的表单。 运行宁 Windows 7 专业的。请求来自 Firebird 数据库的 SQL 数据。响应是 JSON。所有流量都是 HTTP。
挑战
当我对少量用户进行测试时,一切都运行良好。现在我已经将它推广到大约 400 个用户 沟通问题。服务器停止响应请求,我可以让它再次响应的唯一方法是重新启动它正在 运行ning 的机器,然后重新启动它。期间需要更频繁地重新启动 高音量时间。
症状
使用 Windows netstat 我注意到每当出现 CLOSE_WAIT 类型的 TCP 连接时,服务器就会停止响应请求,我必须重新启动
测试程序
即使服务器上没有流量,我也能够模拟这种挂起。我创建了一个发送多个请求的网页 每个请求之间的延迟。
网页让我指定要发出的请求数、每个请求之间等待多长时间以及超时前等待多长时间。即使在请求之间只有一毫秒的时间,服务器似乎也可以毫无问题地做出响应。
测试结果
如果我将每个请求的超时时间设置为一个非常小的数字,比如 1 毫秒,我可以让我的 Delphi HTTP 服务器挂起。正如我所料,在 1 毫秒超时时,对我的服务器的请求每次都会失败。超时时间太短,我的服务器可能无法足够快地响应。
我不明白的是,在我在客户端强制超时后,即使请求数量相对较少(少于 50 个),我的 Delphi 网络服务器也不再响应任何请求.当我在服务器机器上 运行 netstat 时,有许多 CLOSE_WAIT 套接字连接。即使在一个小时后并关闭我的服务器后,CLOSE_WAIT 套接字连接仍然存在。
问题
这是怎么回事?为什么我的 Delphi Indy idHTTPServer 在有(即使只有一个)CLOSE_WAIT 套接字连接时停止响应? CLOSE_WAITs 不会消失,服务器不会再次开始响应。我必须重新启动。
我没有做什么?
这是显示 CLOSE_WAITs 的 netstat 命令的结果:
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
这是我的网络服务器的精华:
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.
此代码至少存在两个可能导致崩溃的主要问题:
数据库和事务对象对于
IdHTTPServer
创建的所有线程都是全局的。当您断开数据库连接时,它会断开所有线程。如果在分配内容文本时出现 运行 时间错误,此行
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.