[英]Indy TIdTcpServer input buffer empty
我有一个监听 tcp 客户端连接的应用程序。 当客户端连接时,客户端会发送大量数据,在一个示例中为 100+k。 Indy TCPServer 接收它,处理/重新格式化数据,将其发送到云中的 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; 执行方法触发,但有一个 -0 大小的输入缓冲区。 为了安全起见,我将接收缓冲区设置为 256k。 这是我正在记录的内容:
AContext.Connection.IOHandler.InputBufferAsString
AContext.Connection.IOHandler.RecvBufferSize
AContext.Connection.IOHandler.InputBufferIsEmpty
ReceiveBuffer 记录为 262144。 InputBufferIsEmpty 总是返回 true:当然 InputBufferAsString.= ''。
所以我想知道这是否可能是应用程序运行的服务器/网络/域上的安全“功能”? 我已经在域上的两台不同服务器上对其进行了测试,结果完全相同。 客户端连接,记录它正在发送,IdTcpServer 记录连接,但什么也没收到。
任何想法或建议将不胜感激! TIA
首先,默认的 Windows Indy 套接字缓冲区大小为 32 KB。 所以,当你想把所有东西都发送出去(我不喜欢)时,让你发送的数据包更小。 因此,您也可以更快地检查您的连接是否存在超时或其他缺点。 BTW 问题:您使用什么版本的 Indy - 版本 9 或 10?
其次,检查您读取/写入“请求”和“响应”的服务器/客户端代码 - 当您处于文本模式时,读取 function 应以 \r\n 结尾。
第三,检查您是否有非阻塞或阻塞服务器代码。 这意味着 - 您应该为每个连接安装一个 TThread 以正确处理所有连接(可能具有其他属性)。 因此,连接不会与现有的先前连接重叠。
第四(对于高级开发人员):您应该考虑与每个人建立“第二个”连接 - 这可以帮助保持线路(跳跃......
第五:(对于高级开发人员):您应该牢记安全方面。 这意味着,您应该为您的连接使用 SSL 证书(Indy 为此提供了简单的点击和编辑组件。您的任务就是创建“自签名证书”(PuttyGen(在 Windows 上)或 Linux - 有关详细信息,请参阅 google ) - 如果您拥有您的公共证书,则不需要此步骤 - 查看“Lets Encrypt”(公共免费 SSL 权限,但请注意:您必须每三个月更新开发者 SSL 证书(一个 certbot 脚本)帮助你做所有这些事情)。
如果您有任何问题,请随时提出,我会尽力帮助您。
圣诞快乐
try..except
TCPConnect()
中的 try..except 没用。 设置ReadTimeout
不会引发异常。 并且在任何情况下,如果未捕获的异常逃脱了OnConnect
或OnException
事件,服务器将自动断开客户端。
FMDMStr
没有以线程安全的方式使用。 每个TIdContext
在自己的线程中运行。 不要在没有适当同步的情况下跨线程边界共享变量。 在这种情况下,如果只有 1 个客户端连接(并且您通过设置TIdTCPServer.MaxConnections=1
来强制执行),那么就这样吧。 否则, FMDMStr
根本不应该是 class 成员变量,而应该是TCPExecute()
的局部变量,以便每个连接的客户端都在自己的string
上运行。
需要删除TransferTCPBytesToString()
中的InputBufferIsEmpty
检查。 让WaitFor()
阻塞,直到数据到达或超时。 此外,由于您设置AInclusive=True
, WaitFor()
的返回值将始终在末尾包含#28#13
,因此Pos()
检查是无用的,应该删除。 此外,由于TransferTCPBytesToString()
总是返回一个以#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);
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...
MDMStr := 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()
在运行时准备好。
只是要考虑的事情...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.