从 ClientSocket (Mobile) 向 ServerSocket (Server) 发送数据时数据丢失

Data missing while sending data from ClientSocket (Mobile) to ServerSocket (Server)

我在 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 字段。

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

Sending the right record size over socket

您对 ExtractToBytes() 的使用是完全错误的。该方法 returns 在那个特定时刻 任意 字节存储在 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;