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

时间:2017-05-09 18:07:00

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

我在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

1 个答案:

答案 0 :(得分: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;