For example, if a struct contains a char a followed by an int b, typically b would be at offset 4, even though sizeof(a) is 1.
To get tight packing, you need to explicitly tell the compiler to disable padding.
In some cases you could manually pad your structs to satisfy lowest common denominator architecture requirements, and use compiler extensions mark them as packed (which should be a noop on the architectures you've considered), if the protocol padding/alignment is compatible with those requirements. But the code still theoretically could break on ABIs you didn't consider.
Manually packing and unpacking bytes is the safest way to do things in production.
Of course there is __attribute__((packed)) and __attribute__((aligned(8)))
Also a lot requests have dynamic size and cannot be as easily packed and serialized to be sent over the "wire". So for example you could put pointer to string into struct, malloc required space, then assign pointer. After when you want to send data you would have to write serialization methods to put it in correct order. It is all fine and should be done when you build bulletproof system to manage X11. But here these techniques just draws too much attention away from X11.
C structs have packing, alignment, and endianness gotchas that can be portably addressed with byte-level de/ser.
Example: https://gist.github.com/cd089675a6088b0b8482c005a0e3897a