Delphi 从ClientSocket(移动)向ServerSocket(服务器)发送数据时丢失数据

Delphi 从ClientSocket(移动)向ServerSocket(服务器)发送数据时丢失数据,delphi,firemonkey,vcl,delphi-10.1-berlin,delphi-10.2-tokyo,Delphi,Firemonkey,Vcl,Delphi 10.1 Berlin,Delphi 10.2 Tokyo,我在Delphi10.1 Berlin中使用FireMonkey开发Android移动客户端应用程序,在Delphi10.1 Berlin中使用VCL开发Windows服务器应用程序 在移动应用程序中,我使用TIdTCPClient发送以下记录: PSampleReq = ^TSampleReq ; TSampleReq = packed record Value1: array [0..10] of Char; Value2: array [0..59] of Char; Valu

我在Delphi10.1 Berlin中使用FireMonkey开发Android移动客户端应用程序,在Delphi10.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)));
在读取服务器应用程序中的数据时,我无法读取
Value5
Value6
Value7
字段。下面是读取数据的代码:

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;
在此之后,我将处理队列中的数据,以及我在下面提到的处理方法:

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字段

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


您使用的
提取字节()
是完全错误的。该方法返回在特定时刻存储在
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
  ReceivedIDBytes: TServerRecord;
begin
  AContext.Connection.IOHandler.ReadBytes(ReceivedIDBytes.PointerMessage.tyTIDBytes, AContext.Connection.IOHandler.ReadInt32);  // <-- HERE!!!
  ReceivedIDBytes.ClientSocket := AContext.Connection;
  MessageProcessorThread.ProcessMessageQueue.Enqueue(ReceivedIDBytes);
end;
var
ReceivedIDBytes:TServerRecord;
开始

AContext.Connection.IOHandler.ReadBytes(ReceivedIDBytes.PointerMessage.tyTIDBytes,AContext.Connection.IOHandler.ReadInt32);//您是否验证了两个应用程序中的
sizeof(TSampleReq)
正好是276字节?在调用
Move()
之前,您没有显示正在填充
tyTIDBytes
的实际读取代码。代码应该是这样的:
var tyTIDBytes:TIdBytes。。。AContext.Connection.IOHandler.ReadBytes(tyTIDBytes,SizeOf(TSampleReq),False)仅供参考,Indy有一个
BytesToRaw()
函数作为
RawToBytes()
BytesToRaw(tyTIDBytes,sampleeq,SizeOf(TSampleReq))和仅供参考,您没有使用其中描述的“优化技术”。这种技术在发送实际数据之前先发送数据大小。Indy的等价物看起来是这样的:
tyTIDBytes:=RawToBytes(sampleeq,SizeOf(tsampleq);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));
请您的问题显示您在连接两端使用的实际代码。发送代码和读取代码之间显然不匹配。您没有回答我之前的问题:“您验证了
SizeOf(TSampleReq)吗?”
两个应用程序中的字节数都是276吗?”。我在问题中添加了额外的信息,从服务器接收的部分应该针对不同的记录进行处理,我也尝试了你的方法,但仍然面临相同的问题。你没有使用我描述的技术,甚至没有关闭。你使用的
ExtractToBytes()
完全错了,这是您的问题的根源。我现在发布了一个答案。谢谢。当客户端应用程序在桌面上运行时,它可以正常工作,并且没有数据包丢失。但是当我尝试在Android mobile上运行时,没有收到value6。Android firemonkey移动应用程序有任何限制吗?@Work2Enjoy-Enjoy2Work th我展示的e代码在所有平台上都同样有效。如果它不适用于你,那么你的代码中一定还有不匹配的地方。你需要调试你的代码。你仍然没有回答我之前的问题。这是我最后一次问:“你的所有应用程序中的
sizeof(TSampleReq)
正好是276字节吗?”您是否验证了
TIdIOHandler.ReadInt32()
是否每次都返回预期值?如果不是,请确保您使用的是最新版本的Indy.IIRC,Android上有一个endian错误,我不久前修复了该错误。谢谢@Remy。现在我在服务器端收到了正确的数据。需要发送的特定数据包是否有大小限制?@Work2Enjoy-EnjOY2工作限制为2GB,即
Int32
的最大值。
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;