开发者

Send a struct over a socket with correct padding and endianness in C

开发者 https://www.devze.com 2023-02-24 07:58 出处:网络
I have several structures defined to send开发者_运维问答 over different Operating Systems (tcp networks).

I have several structures defined to send开发者_运维问答 over different Operating Systems (tcp networks). Defined structures are:

struct Struct1 { uint32_t num; char str[10]; char str2[10];}
struct Struct2 { uint16_t num; char str[10];}   

typedef Struct1 a;
typedef Struct2 b;

The data is stored in a text file. Data Format is as such:

  • 123
  • Pie
  • Crust

Struct1 a is stored as 3 separate parameters. However, struct2 is two separate parameters with both 2nd and 3rd line stored to the char str[] . The problem is when I write to a server over the multiple networks, the data is not received correctly. There are numerous spaces that separate the different parameters in the structures. How do I ensure proper sending and padding when I write to server? How do I store the data correctly (dynamic buffer or fixed buffer)?

Example of write: write(fd,&a, sizeof(typedef struct a)); Is this correct?

Problem Receive Side Output for struct2:

  • 123( , )
  • 0 (, Pie)
  • 0 (Crust,)

Correct Output

123(Pie, Crust)


write(fd,&a, sizeof(a)); is not correct; at least not portably, since the C compiler may introduce padding between the elements to ensure correct alignment. sizeof(typedef struct a) doesn't even make sense.

How you should send the data depends on the specs of your protocol. In particular, protocols define widely varying ways of sending strings. It is generally safest to send the struct members separately; either by multiple calls to write or writev(2). For instance, to send

struct { uint32_t a; uint16_t b; } foo;

over the network, where foo.a and foo.b already have the correct endianness, you would do something like:

struct iovec v[2];
v[0].iov_base = &foo.a;
v[0].iov_len  = sizeof(uint32_t);
v[1].iov_base = &foo.b;
v[1].iov_len  = sizeof(uint16_t);
writev(fp, v, 2);


Sending structures over the network is tricky. The following problems you might have

  1. Byte endiannes issues with integers.
  2. Padding introduced by your compiler.
  3. String parsing (i.e. detecting string boundaries).

If performance is not your goal, I'd suggest to create encoders and decoders for each struct to be send and received (ASN.1, XML or custom). If performance is really required you can still use structures and solve (1), by fixing an endianness (i.e. network byte order) and ensure your integers are stored as such in those structures, and (2) by fixing a compiler and using the pragmas or attributes to enforce a "packed" structure.

Gcc for example uses attribute((packed)) as such:

struct mystruct {
  uint32_t  a;
  uint16_t b;
  unsigned char text[24];
} __attribute__((__packed__));

(3) is not easy to solve. Using null terminated strings at a network protocol and depending on them being present would make your code vulnerable to several attacks. If strings need to be involved I'd use an proper encoding method such as the ones suggested above.


The easy way would be to write two functions for each structure: one to convert from textual representation to the struct and one to convert a struct back to text. Then you just send the text over the network and on the receiving side convert it to your structures. That way endianness does not matter.


There are conversion functions to ensure portability of binary integers across a network. Use htons, htonl, ntohs and ntohl to convert 16 and 32 bit integers from host to network byte order and vice versa.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号