繁体   English   中英

Indy TIdTcpServer 输入缓冲区为空

[英]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不会引发异常。 并且在任何情况下,如果未捕获的异常逃脱了OnConnectOnException事件,服务器将自动断开客户端。

FMDMStr没有以线程安全的方式使用。 每个TIdContext在自己的线程中运行。 不要在没有适当同步的情况下跨线程边界共享变量。 在这种情况下,如果只有 1 个客户端连接(并且您通过设置TIdTCPServer.MaxConnections=1来强制执行),那么就这样吧。 否则, FMDMStr根本不应该是 class 成员变量,而应该是TCPExecute()的局部变量,以便每个连接的客户端都在自己的string上运行。

需要删除TransferTCPBytesToString()中的InputBufferIsEmpty检查。 WaitFor()阻塞,直到数据到达或超时。 此外,由于您设置AInclusive=TrueWaitFor()的返回值将始终在末尾包含#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.

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