【问题标题】:Data missing while sending data from ClientSocket (Mobile) to ServerSocket (Server)从 ClientSocket (Mobile) 向 ServerSocket (Server) 发送数据时数据丢失
【发布时间】:2017-05-09 18:07:00
【问题描述】:

我在 Delphi 10.1 Berlin 中使用 FireMonkey 开发 Android 移动客户端应用程序,我在 Delphi 10.1 Berlin 中使用 VCL 开发 Windows 服务器应用程序。

在移动应用中,我使用TIdTCPClient发送以下记录:

PSampleReq = ^TSampleReq ;
TSampleReq = packed record
  Value1: array [0..10] of Char;
  Value2: array [0..59] of Char;
  Value3: array [0..40] of Char;
  Value4: Int64;
  Value5: array [0..9] of Char;
  Value6: array [0..9] of Char;
  Value7: Integer;
end;

我已经用数据填充了数据包,并正在使用以下代码发送数据包:

FIdTCPClient.IOHandler.Write(RawToBytes(TSampleReq,SizeOf(TSampleReq)));

在服务器应用程序中读取数据时,我无法读取Value5Value6Value7 字段。下面是读取数据的代码:

Move(tyTIDBytes[0], SampleReq, SizeOf(TSampleReq));

为了接收从客户端套接字发送的数据,我使用了 TIDTcpServer 并在 Execute 方法中处理了以下代码:

TServerRecord = packed record
PointerMessage : TIndyBytes;
ClientSocket   : TIdTCPConnection;
end;

Var    
ReceivedIDBytes: TServerRecord;
begin
if not AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.InputBuffer.ExtractToBytes(ReceivedIDBytes.PointerMessage.tyTIDBytes) ;
ReceivedIDBytes.ClientSocket := AContext.Connection;
MessageProcessorThread.ProcessMessageQueue.Enqueue(ReceivedIDBytes);
end;

在此之后我正在处理来自Queue的数据以及我在下面提到的处理方法:

var
InputRec: TServerRecord;
begin
InputRec := DBWorkerThread.DBWorkerQueue.Dequeue;
MessageHeaderPtr := @InputRec.PointerMessage.tyTIDBytes[0];
iHMMessageCode := StrToIntDef( Trim(MessageHeaderPtr^.MessageCode), UNKNOWN_MESSAGE_CODE);
case iHMMessageCode of
1001:
begin
Move(InputRec.PointerMessage.tyTIDBytes[0], SampleReq, SizeOf(TSampleReq));
end;
end;

在此我无法读取 Value5、Value6 和 Value7 字段。

通过下面的链接,我发现了一些优化技术以及如何正确处理数据包而不会丢失任何数据包。请帮我解决这个问题。

Sending the right record size over socket

【问题讨论】:

  • 您是否确认sizeof(TSampleReq)两个 应用程序中正好是276 字节?在调用Move() 之前,您没有显示填充tyTIDBytes实际 阅读代码。代码应该是这样的:var tyTIDBytes: TIdBytes; ... AContext.Connection.IOHandler.ReadBytes(tyTIDBytes, SizeOf(TSampleReq), False); 而且仅供参考,Indy 有一个 BytesToRaw() 函数作为 RawToBytes() 的伴侣:BytesToRaw(tyTIDBytes, SampleReq, SizeOf(TSampleReq));
  • 而且仅供参考,您没有使用other link 中描述的“优化技术”。该技术在发送实际数据之前发送数据大小。 Indy 等效项如下所示:tyTIDBytes := RawToBytes(SampleReq, SizeOf(TSampleReq); FIdTCPClient.IOHandler.Write(Int32(Length(tyTIDBytes))); FIdTCPClient.IOHandler.Write(tyTIDBytes); ... AContext.Connection.IOHandler.ReadBytes(tyTIDBytes, AContext.Connection.IOHandler.ReadInt32, False); BytesToRaw(tyTIDBytes, SampleReq, SizeOf(TSampleReq));
  • edit您的问题显示您在连接两端使用的实际代码。您的发送代码和阅读代码之间显然不匹配。而且您没有回答我之前的问题:“您是否验证 sizeof(TSampleReq) 在两个应用程序中正好是 276 字节”。跨度>
  • 我在我的问题中添加了额外的信息,并且应该从服务器接收部分处理不同的记录,我也尝试了你的方法,但我仍然面临同样的问题。
  • 你没有使用我描述的技术,甚至没有接近。您对ExtractToBytes() 的使用完全错误,是您问题的根源。我现在已经发布了答案。

标签: delphi firemonkey vcl delphi-10.1-berlin delphi-10.2-tokyo


【解决方案1】:

您对ExtractToBytes() 的使用是完全错误的。该方法返回在那个特定时刻存储在InputBuffer 中的任何任意字节,这可能小于或大于您实际期望的值。

如果您的客户端每次都发送一个固定大小的记录,那么您应该读取的字节数应该是一样多,不多也不少:

var
  ReceivedIDBytes: TServerRecord;
begin
  AContext.Connection.IOHandler.ReadBytes(ReceivedIDBytes.PointerMessage.tyTIDBytes, SizeOf(TSampleReq));  // <-- HERE!!!
  ReceivedIDBytes.ClientSocket := AContext.Connection;
  MessageProcessorThread.ProcessMessageQueue.Enqueue(ReceivedIDBytes);
end;

但是,如果记录的大小取决于消息代码,那么您的客户端应该在发送实际记录字节之前发送记录中的字节数:

var
  tyTIDBytes: TIdBytes;
begin
  tyTIDBytes := RawToBytes(TSampleReq, SizeOf(TSampleReq));
  FIdTCPClient.IOHandler.Write(Int32(Length(tyTIDBytes)));
  FIdTCPClient.IOHandler.Write(tyTIDBytes);
end;

然后服务器可以在读取字节之前读取字节数:

var
  ReceivedIDBytes: TServerRecord;
begin
  AContext.Connection.IOHandler.ReadBytes(ReceivedIDBytes.PointerMessage.tyTIDBytes, AContext.Connection.IOHandler.ReadInt32);  // <-- HERE!!!
  ReceivedIDBytes.ClientSocket := AContext.Connection;
  MessageProcessorThread.ProcessMessageQueue.Enqueue(ReceivedIDBytes);
end;

【讨论】:

  • 谢谢。当客户端应用程序在桌面上运行并且没有数据包丢失时,它可以正常工作。但是当我尝试在 Android 手机上运行时,value6 没有收到。 android firemonkey 移动应用有什么限制吗?
  • @Work2Enjoy-Enjoy2Work 我展示的代码在所有平台上都同样有效。如果它对您不起作用,那么您的代码中一定还有不匹配的地方。你需要调试你的代码。你还没有回答我之前的问题。这是我最后一次要问:“sizeof(TSampleReq) 在你所有的应用程序中正好是 276 字节吗?”你是否确认 TIdIOHandler.ReadInt32() 正在返回每次的期望值?如果没有,请确保您使用的是最新版本的 Indy。 IIRC,我不久前修复了 Android 上的一个字节序错误。
  • 谢谢@Remy。现在我在服务器端接收到正确的数据。对于需要发送的特定数据包是否有任何大小限制?
  • @Work2Enjoy-Enjoy2Work 限制为 2GB,最大值为Int32
猜你喜欢
  • 2019-12-08
  • 1970-01-01
  • 1970-01-01
  • 2021-09-27
  • 1970-01-01
  • 2019-01-29
  • 2015-09-27
  • 2018-01-25
  • 1970-01-01
相关资源
最近更新 更多