Indy TIdTcpServer 输入缓冲区为空
Indy TIdTcpServer input buffer empty
我有一个侦听 TCP 客户端连接的应用程序。当客户端连接时,客户端会发送一大块数据,在一个示例中为 100+k。 Indy TCPServer 接收它,processes/reformats 数据,将其发送到云端的 http 服务器,接收响应,根据响应创建确认,并将其发送回客户端,断开连接。
procedure TMyApp.TCPConnect(AContext: TIdContext);
begin
try
AContext.Connection.Socket.ReadTimeout := 10000;
except
on E:Exception do
AContext.Connection.Disconnect;
end;
end;
procedure TMyApp.TCPDisconnect(AContext: TIdContext);
begin
SetLength(FMDMStr,0);
end;
function TMyApp.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): String;
begin
if AnIOHandler.InputBufferIsEmpty then
Exit;
Result := AnIOHandler.WaitFor(#28#13,True,True);
if Pos(#28#13,Result) = 0 then
Result := Result + #28#13;
end;
procedure TMyApp.WriteTCPResponse(AnIOHandler: TIdIOHandler);
var
bytes: TIdBytes;
begin
BuildACK(FMDMStr,bytes);
AnIOHandler.WriteDirect(bytes, Length(bytes));
end;
procedure TMyApp.TCPExecute(AContext: TIdContext);
begin
try
FMDMStr := TransferTCPBytesToString(AContext.Connection.IOHandler);
// test string for HL/7 terminating characters
if Pos(#28#13, FMDMStr) > 0 then
begin
FormatAndSendHTTPMsg(FMDMStr);
// send the response back to tcp client
WriteTCPResponse(AContext.Connection.IOHandler);
end;
except
on E:Exception do
AContext.Connection.Disconnect;
end;
end;
我已经在不同的场景中使用这个应用程序 2 年多了,它运行良好。我已经在多个 windows 10 & windows 服务器 2016 SE 服务器上对其进行了测试,它运行良好。
最近的部署根本不起作用;同一个客户端发送超过 100k 的数据,连接到 TIdTcpServer; execute 方法触发,但有一个 -0 大小的输入缓冲区。为了安全起见,我已将接收缓冲区设置为 256k。这是我正在记录的内容:
AContext.Connection.IOHandler.InputBufferAsString
AContext.Connection.IOHandler.RecvBufferSize
AContext.Connection.IOHandler.InputBufferIsEmpty
ReceiveBuffer 记录为 262144。InputBufferIsEmpty 总是返回 true!当然还有 InputBufferAsString := ''.
所以我想知道这是否可能是应用 运行 上的 server/network/domain 上的安全“功能”?我已经在域中的两台不同服务器上对其进行了测试,结果完全相同。客户端连接,记录它正在发送,IdTcpServer 记录连接,但什么也没收到。
如有任何想法或建议,我们将不胜感激! TIA
首先,默认 Windows Indy 套接字缓冲区大小为 32 KB。
所以,当你想发送所有东西时(我不喜欢)让你发送的数据包更小。因此,您也可以更快地检查您的连接是否存在超时或其他问题。
顺便问一下:您使用哪个版本的 Indy - 版本 9 还是版本 10?
其次,检查您的 server/client 代码,其中您 read/write “请求”和“响应”存在 - 当您处于文本模式时,读取功能应以 \r\n 结尾。
第三,检查你是否有非阻塞或阻塞服务器代码。
这意味着 - 您应该为每个连接安装一个 TThread 以正确处理所有连接(可能与其他属性一起使用)。因此,连接不会与现有的先前连接重叠。
第四(针对高级开发人员):您应该考虑为每个连接建立一个“第二”连接 - 这有助于保持线路(跳...
第五:(针对高级开发人员):您应该考虑安全方面的问题。这意味着,您应该为您的连接使用 SSL 证书(Indy 为此提供了简单的单击和编辑组件。
您的任务就是创建“自签名证书”(PuttyGen(在 Windows 上)或在 Linux 上 - 详情请参阅 google) - 如果您拥有 public证书,您不需要此步骤 - 查看“让我们加密”(一个 public 免费的 SSL 授权机构,但请注意:您必须每三个月更新一次开发者 SSL 证书(certbot 脚本可帮助您做所有这些事情)。
如果您有任何问题,请随时提出,我会尽力帮助您。
圣诞节快乐
TCPConnect()
中的try..except
没用。设置 ReadTimeout
不会引发异常。在任何情况下,如果未捕获的异常逃脱了 OnConnect
或 OnExecute
事件,服务器将自动断开客户端连接。
FMDMStr
未以线程安全方式使用。每个 TIdContext
都在自己的线程中运行。不要在没有适当同步的情况下跨线程边界共享变量。在这种情况下,如果只有 1 个客户端连接(并且您通过设置 TIdTCPServer.MaxConnections=1
强制执行),那么就这样吧。否则,FMDMStr
根本不应该是 class 成员变量,它应该是 TCPExecute()
的局部变量(或者存储在 TIdContext
中),这样每个连接的客户端都在自己独特的 string
.
上运行
TransferTCPBytesToString()
中的 InputBufferIsEmpty
签入需要删除。 Indy 不会填充 InputBuffer
直到它被告知执行读取操作,其中需要从底层套接字中提取新字节。所以 InputBuffer
将保持为空,直到有东西试图从连接中读取。如果您总是首先检查空的 InputBuffer
,您可能会失去读取数据的机会。让WaitFor()
正常阻塞直到数据到达,或者超时。
此外,由于您正在设置 AInclusive=True
,因此 WaitFor()
的 return 值将始终在末尾包含 #28#13
,因此 Pos()
检查是无用的,应该被删除。此外,由于 TransferTCPBytesToString()
始终 return 是一个末尾带有 #28#13
的字符串,因此 TCPExecute()
中的 Pos()
签入也是无用的。
在 WriteTCPResponse()
中,您根本不应该使用 TIdIOHandler.WriteDirect()
。请改用适当的 TIdIOHandler.Write()
重载。发送 TIdBytes
.
过载
话虽如此,试试这样的东西:
procedure TMyApp.TCPConnect(AContext: TIdContext);
begin
AContext.Connection.IOHandler.ReadTimeout := 10000;
end;
function TMyApp.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): String;
begin
// will throw an exception on timeout...
Result := AnIOHandler.WaitFor(#28#13,True,True);
// alternatively:
// Result := AnIOHandler.WaitFor(#28#13,True,True,nil,10000);
// then you don't need to set the IOHandler.ReadTimeout...
end;
procedure TMyApp.WriteTCPResponse(AnIOHandler: TIdIOHandler; const AMsg: string);
var
bytes: TIdBytes;
begin
BuildACK(AMsg, bytes);
AnIOHandler.Write(bytes);
end;
procedure TMyApp.TCPExecute(AContext: TIdContext);
var
FMDMStr: string;
begin
FMDMStr := TransferTCPBytesToString(AContext.Connection.IOHandler);
// TODO: change FormatAndSendHTTPMsg() to return the response string
// directly, rather than save it in a class member variable...
FMDMStr := FormatAndSendHTTPMsg(FMDMStr);
// send the response back to tcp client
WriteTCPResponse(AContext.Connection.IOHandler, FMDMStr);
end;
仅供参考,在旁注中,您的代码提到了 HL/7。 Indy 有一个 TIdHL7
组件可以在服务器模式下运行,运行 一个内部 TIdTCPServer
读入并响应 HL/7 消息,触发一个 OnReceiveMessage
事件每条消息。在您的场景中,您可以将 TIdHL7.Port
设置为所需的侦听端口,设置 TIdHL7.CommunicationMode=cmSynchronous
,设置 TIdHL7.isListener=True
,将处理程序分配给 TIdHL7.OnReceiveMessage
以发送您的 HTTP 消息,然后调用TIdHL7.Start()
在运行时就绪。
只是需要考虑的事情...
我有一个侦听 TCP 客户端连接的应用程序。当客户端连接时,客户端会发送一大块数据,在一个示例中为 100+k。 Indy TCPServer 接收它,processes/reformats 数据,将其发送到云端的 http 服务器,接收响应,根据响应创建确认,并将其发送回客户端,断开连接。
procedure TMyApp.TCPConnect(AContext: TIdContext);
begin
try
AContext.Connection.Socket.ReadTimeout := 10000;
except
on E:Exception do
AContext.Connection.Disconnect;
end;
end;
procedure TMyApp.TCPDisconnect(AContext: TIdContext);
begin
SetLength(FMDMStr,0);
end;
function TMyApp.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): String;
begin
if AnIOHandler.InputBufferIsEmpty then
Exit;
Result := AnIOHandler.WaitFor(#28#13,True,True);
if Pos(#28#13,Result) = 0 then
Result := Result + #28#13;
end;
procedure TMyApp.WriteTCPResponse(AnIOHandler: TIdIOHandler);
var
bytes: TIdBytes;
begin
BuildACK(FMDMStr,bytes);
AnIOHandler.WriteDirect(bytes, Length(bytes));
end;
procedure TMyApp.TCPExecute(AContext: TIdContext);
begin
try
FMDMStr := TransferTCPBytesToString(AContext.Connection.IOHandler);
// test string for HL/7 terminating characters
if Pos(#28#13, FMDMStr) > 0 then
begin
FormatAndSendHTTPMsg(FMDMStr);
// send the response back to tcp client
WriteTCPResponse(AContext.Connection.IOHandler);
end;
except
on E:Exception do
AContext.Connection.Disconnect;
end;
end;
我已经在不同的场景中使用这个应用程序 2 年多了,它运行良好。我已经在多个 windows 10 & windows 服务器 2016 SE 服务器上对其进行了测试,它运行良好。
最近的部署根本不起作用;同一个客户端发送超过 100k 的数据,连接到 TIdTcpServer; execute 方法触发,但有一个 -0 大小的输入缓冲区。为了安全起见,我已将接收缓冲区设置为 256k。这是我正在记录的内容:
AContext.Connection.IOHandler.InputBufferAsString
AContext.Connection.IOHandler.RecvBufferSize
AContext.Connection.IOHandler.InputBufferIsEmpty
ReceiveBuffer 记录为 262144。InputBufferIsEmpty 总是返回 true!当然还有 InputBufferAsString := ''.
所以我想知道这是否可能是应用 运行 上的 server/network/domain 上的安全“功能”?我已经在域中的两台不同服务器上对其进行了测试,结果完全相同。客户端连接,记录它正在发送,IdTcpServer 记录连接,但什么也没收到。
如有任何想法或建议,我们将不胜感激! TIA
首先,默认 Windows Indy 套接字缓冲区大小为 32 KB。 所以,当你想发送所有东西时(我不喜欢)让你发送的数据包更小。因此,您也可以更快地检查您的连接是否存在超时或其他问题。 顺便问一下:您使用哪个版本的 Indy - 版本 9 还是版本 10?
其次,检查您的 server/client 代码,其中您 read/write “请求”和“响应”存在 - 当您处于文本模式时,读取功能应以 \r\n 结尾。
第三,检查你是否有非阻塞或阻塞服务器代码。 这意味着 - 您应该为每个连接安装一个 TThread 以正确处理所有连接(可能与其他属性一起使用)。因此,连接不会与现有的先前连接重叠。
第四(针对高级开发人员):您应该考虑为每个连接建立一个“第二”连接 - 这有助于保持线路(跳...
第五:(针对高级开发人员):您应该考虑安全方面的问题。这意味着,您应该为您的连接使用 SSL 证书(Indy 为此提供了简单的单击和编辑组件。 您的任务就是创建“自签名证书”(PuttyGen(在 Windows 上)或在 Linux 上 - 详情请参阅 google) - 如果您拥有 public证书,您不需要此步骤 - 查看“让我们加密”(一个 public 免费的 SSL 授权机构,但请注意:您必须每三个月更新一次开发者 SSL 证书(certbot 脚本可帮助您做所有这些事情)。
如果您有任何问题,请随时提出,我会尽力帮助您。
圣诞节快乐
TCPConnect()
中的try..except
没用。设置 ReadTimeout
不会引发异常。在任何情况下,如果未捕获的异常逃脱了 OnConnect
或 OnExecute
事件,服务器将自动断开客户端连接。
FMDMStr
未以线程安全方式使用。每个 TIdContext
都在自己的线程中运行。不要在没有适当同步的情况下跨线程边界共享变量。在这种情况下,如果只有 1 个客户端连接(并且您通过设置 TIdTCPServer.MaxConnections=1
强制执行),那么就这样吧。否则,FMDMStr
根本不应该是 class 成员变量,它应该是 TCPExecute()
的局部变量(或者存储在 TIdContext
中),这样每个连接的客户端都在自己独特的 string
.
TransferTCPBytesToString()
中的 InputBufferIsEmpty
签入需要删除。 Indy 不会填充 InputBuffer
直到它被告知执行读取操作,其中需要从底层套接字中提取新字节。所以 InputBuffer
将保持为空,直到有东西试图从连接中读取。如果您总是首先检查空的 InputBuffer
,您可能会失去读取数据的机会。让WaitFor()
正常阻塞直到数据到达,或者超时。
此外,由于您正在设置 AInclusive=True
,因此 WaitFor()
的 return 值将始终在末尾包含 #28#13
,因此 Pos()
检查是无用的,应该被删除。此外,由于 TransferTCPBytesToString()
始终 return 是一个末尾带有 #28#13
的字符串,因此 TCPExecute()
中的 Pos()
签入也是无用的。
在 WriteTCPResponse()
中,您根本不应该使用 TIdIOHandler.WriteDirect()
。请改用适当的 TIdIOHandler.Write()
重载。发送 TIdBytes
.
话虽如此,试试这样的东西:
procedure TMyApp.TCPConnect(AContext: TIdContext);
begin
AContext.Connection.IOHandler.ReadTimeout := 10000;
end;
function TMyApp.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): String;
begin
// will throw an exception on timeout...
Result := AnIOHandler.WaitFor(#28#13,True,True);
// alternatively:
// Result := AnIOHandler.WaitFor(#28#13,True,True,nil,10000);
// then you don't need to set the IOHandler.ReadTimeout...
end;
procedure TMyApp.WriteTCPResponse(AnIOHandler: TIdIOHandler; const AMsg: string);
var
bytes: TIdBytes;
begin
BuildACK(AMsg, bytes);
AnIOHandler.Write(bytes);
end;
procedure TMyApp.TCPExecute(AContext: TIdContext);
var
FMDMStr: string;
begin
FMDMStr := TransferTCPBytesToString(AContext.Connection.IOHandler);
// TODO: change FormatAndSendHTTPMsg() to return the response string
// directly, rather than save it in a class member variable...
FMDMStr := FormatAndSendHTTPMsg(FMDMStr);
// send the response back to tcp client
WriteTCPResponse(AContext.Connection.IOHandler, FMDMStr);
end;
仅供参考,在旁注中,您的代码提到了 HL/7。 Indy 有一个 TIdHL7
组件可以在服务器模式下运行,运行 一个内部 TIdTCPServer
读入并响应 HL/7 消息,触发一个 OnReceiveMessage
事件每条消息。在您的场景中,您可以将 TIdHL7.Port
设置为所需的侦听端口,设置 TIdHL7.CommunicationMode=cmSynchronous
,设置 TIdHL7.isListener=True
,将处理程序分配给 TIdHL7.OnReceiveMessage
以发送您的 HTTP 消息,然后调用TIdHL7.Start()
在运行时就绪。
只是需要考虑的事情...