Recent

Author Topic: Reading n-Bytes from TCP - How to convert AnsiStr to Bytes and vice versa?  (Read 2077 times)

FrankBKK

  • New Member
  • *
  • Posts: 22


Hope somebody can help me with this problem - I have to read data from an interface that is specified like this:

<Each message consists of a 2-byte length (in network byte order) followed by that many bytes of data. The end of the msg series is indicated by an empty message (length of 0).>

Using TDataPortTCP I can read the buffer with Dataport.Peek(size) and pull the data from the buffer with Dataport.Pull(size) - both methods provide the result as AnsiStr, the data itself (except for the length-info) is in plain ASCII, each line terminated by #10

I imagine that something like this should work, but I have no idea how to convert AnsiStr to Bytes and vice versa:

Code: Pascal  [Select][+][-]
  1.    while DataPortTCP.Peek(2) > ZeroBytes do
  2.    begin
  3.       LengthInBytes := DataPortTCP.Pull(2) ;
  4.       sContent := DataPortTCP.Pull(LengthInByte) ;
  5.       // do something with sContent
  6.    end;

How do I declare / get / convert the pseudo-vars ZeroBytes and LengthInBytes and how do I have to deal with Endianess ?

Unfortunately I know nothing about TBytes etc. and what I read so far did only lead to more confusion ;-)

I would be very grateful if someone could point me into the right direction.

engkin

  • Hero Member
  • *****
  • Posts: 2858
To change the endianness use BEToN or LEToN.
To turn a string into an array of bytes, use BytesOf from unit SysUtils.

This would be more like:
Code: Pascal  [Select][+][-]
  1. uses
  2. ...
  3.   SysUtils;
  4. ...
  5. var
  6.   LengthInBytes:Word;//<--Two bytes;
  7.   sContent:AnsiString;
  8.   Bytes: TBytes;
  9. begin
  10.   ..
  11.   while DataPortTCP.Peek(2) > ZeroBytes do
  12.   begin
  13.      LengthInBytes := DataPortTCP.Pull(2) ;
  14.      sContent := DataPortTCP.Pull(LengthInByte) ;
  15.      // do something with sContent
  16.      LengthInBytes:=BEToN(LengthInBytes);
  17.      Bytes:=BytesOf(sContent);
  18. ...
  19.   end;

TBytes is an array of byte:
The first value is Bytes[0], the second one is Bytes[1], ...etc

You can access the ASCII chars instead:
The first char is sContent[1], the second one is sContent[2], ...etc
« Last Edit: May 25, 2021, 09:29:14 pm by engkin »

FrankBKK

  • New Member
  • *
  • Posts: 22
Thanks for your feedback, that helped me understand the issue better !

Code: Pascal  [Select][+][-]
  1. LengthInBytes := DataPortTCP.Pull(2) ;
   

unfortunately this does not work as Pull returns an AnsiString whereas LengthInBytes is Word

I still need help in figuring out
- how to convert this string into bytes that show the proper length
- then convert that back into an integer so I can pull the next batch of data with DataPortTCP.Pull(length).


Let me try to explain:

DataPortTCP.Pull(n) takes <n> characters / bytes out of the buffer and returns them as AnsiString.
DataPortTCP.Pull() takes ALL chars out of the buffer.

The messages I receive have the format    LengthOfMessage + Message   where LengthOfMessage are two bytes,
Message are in ASCII127  e.g. <#00#06a text>
The end of all messages sent is defined as a message with values  <#0#0>

I can read the whole buffer in one shoot by ignoring the #0's and extract anything > #31 - unfortunately that gives me some garbage when the message is longer than 31 chars as the two bytes at the begin of each msg that contain the msg length then display as standard ASCII e.g. <#00#65a very long text with sixty five chars here ....>
which converts to <Aa very long text...>

So the only way to get ungarbled msgs is to read the two bytes length n, convert that to an integer then  get the next n chars and repeat until #0#0 is received
And that's where I got lost -  I have no idea how I can extract and convert the length of the line as a proper value





dseligo

  • Sr. Member
  • ****
  • Posts: 315
Try something like this:
Code: Pascal  [Select][+][-]
  1. var strLengthInBytes:String;
  2.     sContent:String;
  3.     LengthInBytes:Integer;
  4. ...
  5.        while DataPortTCP.Peek(2) > ZeroBytes do
  6.        begin
  7.           strLengthInBytes := DataPortTCP.Pull(2) ;
  8.           // I don't know if you need to check if you actually received two characters or are you
  9.           // sure that you will always have two characters here
  10.           LengthInBytes := Ord(strLengthInBytes[1])*256+Ord(strLengthInBytes[2]);
  11.           sContent := DataPortTCP.Pull(LengthInBytes);
  12.           // do something with sContent
  13.        end;

FrankBKK

  • New Member
  • *
  • Posts: 22
Thanks everybody for all your valuable input and suggestions - I learned a lot !

While playing around with all the suggested solutions I came up with another way that seems to be considerable faster and avoids all those pesky issues with Byte / String conversions:

Code: Pascal  [Select][+][-]
  1. var
  2.    i : LongInt ;
  3.    iBegin, iLength : Integer ;
  4.    sLine, sTimeStamp, sEvent : String ;
  5.    gsRawEvents : AnsiString ;
  6. begin
  7.    // get all messages in one shot
  8.    gsRawEvents := gsRawEvents + DataPortTCP.Pull() ;
  9.  
  10.    // start extracting from the first char after the 2-byte length-info
  11.    iBegin := cByteLen + 1 ;
  12.  
  13.    // go through the whole string, char by char
  14.    for i := 1 to Length(gsRawEvents) do
  15.    begin
  16.       // we found a linefeed -> a msg ends here
  17.       if gsRawEvents[i] = #10 then
  18.       begin
  19.          // we extract that line
  20.          iLength := i - iBegin ;
  21.          sLine := sm.Copy(gsRawEvents,iBegin,iLength) ;
  22.  
  23.          // separate Timestamp and Event-Text
  24.          sTimeStamp := sm.Copy(sLine, 1, cTsLength ) ;
  25.          sEvent     := sm.Copy(sLine, cTsLength + cTsBlank ) ;
  26.  
  27.          // set the begin of the next msg line - we ignore the first two chars
  28.          // as they contain only the length of the next msg as byte values
  29.          // which contain control codes - we don't want them in our text
  30.          iBegin := i + cByteLen + 1 ;
  31.       end;
  32.    end;
  33. end;

engkin

  • Hero Member
  • *****
  • Posts: 2858
For the future if needed something similar. Here is how to do it using absolute:
Code: Pascal  [Select][+][-]
  1. var
  2.   LengthInStr:ShortString;
  3.   LengthInBytes:Word absolute LengthInStr[1];
  4. begin
  5. ...
  6.     LengthInStr:=DataPortTCP.Pull(2);
  7.     LengthInBytes:=BEtoN(LengthInBytes);
  8. ...

LengthInBytes would occupy the same first two bytes of LengthInStr.

 

TinyPortal © 2005-2018