Search code examples
delphifiremonkeyvcldelphi-10.1-berlindelphi-10.2-tokyo

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


I'm using FireMonkey in Delphi 10.1 Berlin for developing an Android mobile client application, and I'm using VCL in Delphi 10.1 Berlin for developing a Windows server application.

In the mobile application, I am using TIdTCPClient for sending the following record:

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;

I have filled the packet with data and am sending the packet using the following code:

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

While reading the data in the Server application, I am not able to read the Value5, Value6and Value7 fields. Below is the code that is reading the data:

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

For receiving the data which is send from the client socket, I have used the TIDTcpServer and handled the below code in Execute method:

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;

After this I'm processing the data from Queue and the processing method I have mentioned below:

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;

And in this I'm not able to read the Value5, Value6 and Value7 fields.

With the below Link, I have found some optimized technique and how I can handle the packets properly without any packet missing. Please help me out to resolve this issue.

Sending the right record size over socket


Solution

  • Your use of ExtractToBytes() is completely wrong. That method returns whatever arbitrary bytes are stored in the InputBuffer at that particular moment, which may be less than, or more than, what you are actually expecting.

    If your client is sending a fixed-sized record each time, you should be reading exactly that many bytes, no more, no less:

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

    However, if the size of the record depends on the message code, then your client should send the number of bytes in a record before sending the actual record bytes:

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

    And then the server can read the byte count before reading the bytes:

    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;