TIdTCPServer OnExecute 触发多次

TIdTCPServer OnExecute fires multiple times

我有一个应用程序从另一个应用程序接收 TCP 套接字连接,处理通过套接字流式传输的内容,基于接收到的数据构建请求,使用 TIdHTTP 将请求发送到外部服务器,处理响应,并将 ack 发送回 tcp 客户端。

procedure TMyClass.TCPExecute(AContext: TIdContext);
var
  s: AnsiString;
begin
  s := TransferTCPBytesToString(AContext.Connection.IOHandler);
  // test string for terminating characters
  if Pos(#28#13, s) > 0 then
  begin
  //********PROBLEM: THIS WILL BE CALLED MULTIPLE TIMES 
  //********WITH THE SAME COMPLETE DATA STREAM CONTENT!
    BuildAndSendExternalRequest(s);
    AContext.Connection.IOHandler.InputBuffer.Clear;
    // send the response back to tcp client
    WriteTCPResponse(AContext.Connection.IOHandler);
  end;
end;

function TMyClass.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): AnsiString;
begin
  if AnIOHandler.InputBufferIsEmpty then
    Exit;
  // read message from tcp client connection
  AnIOHandler.ReadBytes(FTCPBytes, AnIOHandler.InputBuffer.Size, True);
  SetLength(Result, Length(FTCPBytes));
  // copy message bytes to string
  Move(FTCPBytes[0], Result[1], Length(FTCPBytes));
end;

procedure TMyClass.WriteTCPResponse(AnIOHandler: TIdIOHandler);
var
  bytes: TBytes;
begin
  // FTCPBytes contains data from tcp client
  // bytes will contain processed response from external server
  BuildACK(FTCPBytes,bytes);
  // FTCPBytes now contains ACK data
  AnIOHandler.WriteDirect(FTCPBytes, Length(FTCPBytes));
end;

request/response 循环成功完成;发送回 tcp 客户端请求者的响应是正确的,进程按预期执行和完成。

但是,因为 OnExecute 事件多次触发相同的数据,所以接收、构建、发送、确认等两个请求。

在查看 tcp 客户端应用程序的日志时,日志仅显示一个正在发送的请求。不确定这里发生了什么。但我需要确保 request/response 循环是 1-1,而不是 1-many!

谢谢。

the OnExecute event fires multiple times ...

它应该是,因为它是一个 循环 事件。只要连接处于活动状态,循环就会运行。 OnExecute 事件处理程序中的代码负责决定每次循环迭代中发生的事情。

理想的情况是事件处理程序从客户端收到1条消息,向客户端发回1条响应,然后退出,这样下一次循环迭代就绪处理下一个 request/response 对。

根据您的描述,您正在尝试这样做,但显示的代码处理得不是很好。最值得注意的是,该代码并未考虑 TCP 的字节流性质。它不区分一条传入消息和另一条传入消息。它只是将整个 IOHandler.InputBuffer 内容原样转储到 AnsiString(为什么是 AnsiString?),如果 AnsiString 恰好包含分隔符 #28#13那么您正在处理整个 AnsiString 然后将其丢弃,从而丢失定界符之后收到的所有内容(即下一条消息的一部分)。如果 AnsiString 不包含定界符,那么您仍然会丢弃 AnsiString,从而在定界符到达之前丢失当前消息到目前为止收到的所有数据。

如果您的所有消息都由 #28#13 分隔,那么我建议您使用 IOHandler.ReadLn()IOHandler.WaitFor() 方法正确阅读每条消息。

此外,您正在为输入和输出重新使用相同的 FTCPBytes 成员,这本身就是错误的,但是如果您有多个客户端,您也没有为其提供任何并发访问保护存在连接。

话虽如此,请尝试更类似的方法:

procedure TMyClass.TCPConnect(AContext: TIdContext);
begin
  AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
end;

procedure TMyClass.TCPExecute(AContext: TIdContext);
var
  s: String;
  response: TIdBytes:
begin
  s := TransferTCPBytesToString(AContext.Connection.IOHandler);
  BuildAndSendExternalRequest(s, response);
  // send the response back to tcp client
  WriteTCPResponse(AContext.Connection.IOHandler, response);
end;

function TMyClass.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): String;
begin
  // read message from tcp client connection
  Result := AnIOHandler.ReadLn(#28#13);
  or:
  Result := AnIOHandler.WaitFor(#28#13);
end;

procedure TMyClass.BuildAndSendExternalRequest(const Msg: String; var Response: TIdBytes);
begin
  // send Msg to external server as needed...
  // store response bytes in Response...
end;

procedure TMyClass.WriteTCPResponse(AnIOHandler: TIdIOHandler; const Response: TIdBytes);
var
  bytes: TIdBytes;
begin
  // Response contains processed response from external server
  BuildACK(bytes, Response);
  // bytes now contains ACK data
  AnIOHandler.Write(bytes);
end;

... with the same data

鉴于您显示的代码,这是不可能的。 TransferTCPBytesToString() 中对 IOHandler.ReadBytes() 的调用正在读取并丢弃 IOHandler.InputBuffer 中的所有字节,因此当调用 TransferTCPBytesToString() 时,相同的字节不可能仍然存在于该缓冲区中稍后再说。因此,您一遍又一遍地看到相同数据的唯一方法是,如果客户端从一开始就一遍又一遍地发送相同的数据。