0

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

Community
  • 1
  • 1
  • Did you validate that `sizeof(TSampleReq)` is exactly 276 bytes in *both* apps? You did not show your *actual* reading code that is populating `tyTIDBytes` before you call `Move()` on it. The code should be something like this: `var tyTIDBytes: TIdBytes; ... AContext.Connection.IOHandler.ReadBytes(tyTIDBytes, SizeOf(TSampleReq), False);` And FYI, Indy has a `BytesToRaw()` function as a companion to `RawToBytes()`: `BytesToRaw(tyTIDBytes, SampleReq, SizeOf(TSampleReq));` – Remy Lebeau May 09 '17 at 19:12
  • And FYI, you are not using the "optimized technique" described in that [other link](http://stackoverflow.com/questions/41131478/). That technique sends the data size before sending the actual data. The Indy equivalent would look something like this: `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));` – Remy Lebeau May 09 '17 at 19:17
  • please [edit] your question to show the *actual* code you are using on both ends of the connection. There is clearly a mismatch between your sending code and your reading code. And you did not answer my earlier question: "*Did you **validate** that `sizeof(TSampleReq)` is **exactly 276 bytes** in both apps?*". – Remy Lebeau May 22 '17 at 16:17
  • I have added extra information in my question and from the server receiving part should be handled for different records and also I have tried your method but still I'm facing the same problem. – Work 2 Enjoy - Enjoy 2 Work May 22 '17 at 17:04
  • you are NOT using the technique I described, not even close. Your use of `ExtractToBytes()` is all wrong, and is the root of your issue. I have posted an answer now. – Remy Lebeau May 22 '17 at 17:33

1 Answers1

1

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;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thanks. It works fine when the client application runs in the desktop and there is not packet missing. But when I tried to run in Android mobile, _value6_ does not received. And is there any limitation is there for android firemonkey mobile app? – Work 2 Enjoy - Enjoy 2 Work May 23 '17 at 04:03
  • @Work2Enjoy-Enjoy2Work the code I have shown works equally on all platforms. If it is not working for you, there must still a mismatch in your code somewhere. You need to debug your code. You STILL have not answered my earlier question. This is the last time I'm going to ask: "*Is `sizeof(TSampleReq)` **exactly 276 bytes** in all of your apps?*" Did you validate that `TIdIOHandler.ReadInt32()` is returning the expected value every time? If not, make sure you are using an up-to-date version of Indy. IIRC, there was an endian bug on Android that I fixed awhile back ago. – Remy Lebeau May 23 '17 at 06:31
  • Thanks @Remy. Now I'm receiving the proper data in Server side. And is there any size limitation for the particular packets which is need to be send? – Work 2 Enjoy - Enjoy 2 Work May 26 '17 at 02:16
  • @Work2Enjoy-Enjoy2Work it is limited to 2GB, the max value of `Int32`. – Remy Lebeau May 26 '17 at 02:31