PDA

View Full Version : TCP Write Raw data



^NyAw^
22nd November 2007, 16:37
Hi,

I'm trying to write raw data thorugh TCPSocket. I have a struct like (Modbus protocol):


//Modbus header
typedef struct MB_Request
{
uchar function_code;
quint16 start_adr;
quint16 quantity_regs;
};

//Modbus/TCP header, containing the Modbus header
typedef struct MBAP_Header
{
quint16 transaction_id;
quint16 protocol_id;
quint16 len;
uchar unit_id;
};

typedef struct MB_Message
{
MBAP_Header mb_hdr;
MB_Request mb_req;
};


What I don't know is how to send a variable (MB_Message type) as raw data. The documentation show that the data have to be stored as "char *". How have I to do this?

Thanks,

jacek
22nd November 2007, 17:01
You can cast a pointer to the structure to char *, but beware that these structures are aligned and for example size of MB_Request is more than 5 bytes. If you use GCC you can add __attribute__((packed)) to pack them. Also make sure that integers have proper endianness.

^NyAw^
22nd November 2007, 19:00
Hi,



You can cast a pointer to the structure to char *



const char* data = (char*)&message;
int ilen = strlen(data);
tcpSocket->write(data,ilen);


Using this I only recive 1 byte.
Maybe have I to create a "char*" containing the same values of the struct?

Thanks,

jacek
22nd November 2007, 19:26
strlen() is for zero-terminated strings, not for data --- use sizeof message instead.

^NyAw^
22nd November 2007, 22:52
Hi,

Ok, it seems to work now.
When the reciver gets the raw data, I have to cast the data to a struct pointer. Right?

The other question is:


but beware that these structures are aligned and for example size of MB_Request is more than 5 bytes.
Also make sure that integers have proper endianness

What do you mean with that the structures are aligned? And how can I maintain the proper endianess, I think that I'm sending the data as big endian.

Thanks,

jacek
22nd November 2007, 23:36
What do you mean with that the structures are aligned?
Check what sizeof( MB_Request ) returns. It contains only 5 bytes of data, yet it's bigger because of performance reasons (i.e. it contains holes between fields). Most likely the protocol expects packed data, so you have to tell your compiler to treat those structures differently.

http://en.wikipedia.org/wiki/Data_structure_alignment


And how can I maintain the proper endianess, I think that I'm sending the data as big endian.
QSysInfo::ByteOrder will tell you whether you are using big-endian or little-endian system and you have to convert values to endianness required by the protocol.

^NyAw^
23rd November 2007, 09:18
Hi,



Check what sizeof( MB_Request ) returns. It contains only 5 bytes of data


It returns me "14". The structs are 7 bytes(Modbus/TCP header) + 5 bytes (Modbus header) that sum 12. So I think that the struct "MB_Message" contains 1 byte for the first struct and another byte for the second struct.

Thanks,

^NyAw^
23rd November 2007, 09:55
Hi,

Ok, thanks for the link of "Data structure alignment".

So, the "Modbus/TCP header" is 7 bytes. The "Modbus header" is 5 bytes. A total of 12 bytes that are 12*8 = 96 bits that are 32 bit aligned.
So, why "sizeof(MB_Message)" is returning me 14 bytes?

Thanks,

jacek
23rd November 2007, 11:10
So, why "sizeof(MB_Message)" is returning me 14 bytes?
Because there are one-byte holes after unit_id and function_code fields.

I've attached a small application. It prints the addresses of all fields, so you can see how they're placed in the memory. If you use GCC, you can uncomment the #define in line 7 and comment out the one in line 6 to see how unaligned (a.k.a. packed) structures look like.

^NyAw^
23rd November 2007, 12:31
Hi,

Ok, so every struct have to be 32-bit aligned.

Thanks,

jacek
23rd November 2007, 12:43
Ok, so every struct have to be 32-bit aligned.
Yes, but only in memory on 32-bit (and pseudo-64bit) machines. Other machines might have different alignment. Also usually you don't align data in files* or in network communication.

* Unless the file format was designed for fast access.

^NyAw^
23rd November 2007, 12:50
Hi,



Yes, but only in memory on 32-bit (and pseudo-64bit) machines


Of course, of course. Them are 32-bit aligned because of using 32-bit processor.

Another question. I'm compiling the program with Visual Studio 2003 .NET and when using GNU/Linux I will have to compile with GCC, will the behavior be different? I'm asking this because I don't know what "__attribute__((packed))" do.

Thanks,

jacek
23rd November 2007, 12:56
I'm compiling the program with Visual Studio 2003 .NET and when using GNU/Linux I will have to compile with GCC, will the behavior be different? I'm asking this because I don't know what "__attribute__((packed))" do.
__attribute__((packed)) is a GCC extension, so it won't work with VS. Basicly it sets the packed attribute for given class, which tells the compiler not to align it. It's perfect when you want to sent your structure directly over the network or read/write it from/to a file, but it's also completely unportable.

VS probably has something different that will allow you to pack the structure. Since it's .NET, it might have a form of some annotation.

^NyAw^
23rd November 2007, 13:05
Hi,

Ok, so it's only important because of the data that will be sent becomes longer and it could be smaller. So if I force to not pack, send it, and recive to another program that loads the data into the struct, the struct will contain the same data.
But if forcing not to align the data, when using the variables in memory will produce TLB miss or a page faults (from the link of "Data structure alignment").

Thank you very much,

jacek
23rd November 2007, 13:20
Ok, so it's only important because of the data that will be sent becomes longer and it could be smaller.
It isn't about sending few bytes less, but about adhering to given protocol. If it requires you to send a one-byte field followed by two-byte one, you can't send 1B of data, 1B of garbage and then 2B of data. If you use some custom protocol and both sender and receiver follow the same rules of alignment, you don't have to worry about it, but if you want to implement Modbus, you have to follow its specification and there are no holes in Modbus messages.


But if forcing not to align the data, when using the variables in memory will produce TLB miss or a page faults
Yes, some CPUs can't even fetch unaligned data from memory, so accesses to such structures might be split into several operations.

^NyAw^
23rd November 2007, 15:24
Hi,

Searching for the web I found this two helpful links (for Visual Studio compiler):
http://msdn2.microsoft.com/en-us/library/83ythb65(VS.80).aspx
http://msdn2.microsoft.com/en-us/library/2e70t5y1(VS.80).aspx

The second one works for me:


#pragma pack(1)
struct MB_Request
{
uchar function_code;
quint16 start_adr;
quint16 quantity_regs;
};

#pragma pack(1)
struct MBAP_Header
{
quint16 transaction_id;
quint16 protocol_id;
quint16 len;
uchar unit_id;
};

#pragma pack(1)
struct MB_Message
{
MBAP_Header mb_hdr;
MB_Request mb_req;
};


Then, executing your example code, the structs are unaligned as I need for Modbus protocol.

Could I use "#ifndef" statment to know wich compiler is used to then use "#pragma pack(1)" or "__attribute__((packed))" depending on compiler?
So when I use GCC on GNU/Linux(or windows also) it will pack the data unaligned as expected. Could use GCC with Visual Studio?

Thanks,

Thanks,

jacek
23rd November 2007, 15:37
Could I use "#ifndef" statment to know wich compiler is used to then use "#pragma pack(1)" or "__attribute__((packed))" depending on compiler?
Yes, but using __desclspec(align(1)) might be more cleaner, because you will need only two macros (depending on the compiler one of them will be empty), instead of ifdef before each structure.


Could use GCC with Visual Studio?
AFAIR it's possible, but I don't use VS, so I can't help you with that.

^NyAw^
23rd November 2007, 15:50
Hi,



Yes, but using __desclspec(align(1)) might be more cleaner


I've tryied this:


__declspec(align(1)) struct MB_Request
{
uchar function_code;
quint16 start_adr;
quint16 quantity_regs;
};

int main()
{
int iA1 = __alignof(MBAP_Header); // This returns "2" byte alignment and using #pragma pack(1) returns me "1" " byte alignment
}




instead of ifdef before each structure


I've found that GCC also supports this #pragma for compatibility with Win32:
http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html

Thanks,

jacek
23rd November 2007, 16:22
__declspec(align(1)) struct MB_Request
...
int iA1 = __alignof(MBAP_Header); // This returns "2" byte
Have you placed __declspec before MBAP_Header too?


I've found that GCC also supports this #pragma for compatibility with Win32
In that case, you don't need any ifdefs.

^NyAw^
23rd November 2007, 16:38
Hi,



Have you placed __declspec before MBAP_Header too?


Yes:


//#pragma pack(1)
__declspec(align(1)) struct MB_Request
{
uchar function_code;
quint16 start_adr;
quint16 quantity_regs;
};

//#pragma pack(1)
__declspec(align(1)) struct MBAP_Header
{
quint16 transaction_id;
quint16 protocol_id;
quint16 len;
uchar unit_id;
};

//#pragma pack(1)
__declspec(align(1)) struct MB_Message
{
MBAP_Header mb_hdr;
MB_Request mb_req;
};


But


int iA1 = __alignof(MBAP_Header);
int iA2 = __alignof(MB_Request);
int iA3 = __alignof(MB_Message);

Returns always "2" for iA1, iA2 and iA3.

Don't worry about it, using "#pragma pack(1)" is going well.

Thanks for your time and your knowdelege.