Packet data structure?

I'm designing a game server, and I've never done anything like this before. I'm just wondering what good structure for a package would be related to data? I use TCP if that matters. Here is an example and what I was considering using at the moment:

(each value in parentheses is a byte)

[Packet length][Action ID][Number of Parameters] [Parameter 1 data length as int][Parameter 1 data type][Parameter 1 data (multi byte)] [Parameter 2 data length as int][Parameter 2 data type][Parameter 2 data (multi byte)] [Parameter n data length as int][Parameter n data type][Parameter n data (multi byte)] 

As I said, I really never did anything like this before what I had above could be a complete bull, which is why I ask;). Also, is the total packet length also required?

+4
source share
5 answers

Transferring the total packet length is a good idea. It may cost another two bytes, but you can peek and wait for the socket to have a full packet ready to be swallowed before it is received. This simplifies the code.

In general, I agree with brazzy, the serialization mechanism provided by the language is preferable to any home-made one.

Other than that (I think you are using C-ish without serialization), I would put the packet identifier as the first data in the packet data structure. IMHO, that some kind of agreement, because the first member of the structure data is always at position 0, and any structure can be omitted to it, identifying anonymous data.

Your compiler may or may not create packaged structures, but in this way you can allocate a buffer, read a package, and then either group the structure depending on the first data element. If you are unlucky and it does not create packed structures, be sure to create a serialization method for each structure that will be built from (obviously inappropriate) memory.

Endiannes is a factor, especially in C-like languages. Be sure to ensure that packages always have the same continent, or that you can identify another end user based on a signature or something else. A strange thing that is very cool: C # and .NET always seem to store data in a little-endian convention when you access it using it as described in this post here. Found that when porting such an application in Mono to SUN. Cool, but if you have this setting, you should still use C # serialization tools.

In addition, your installation looks very good!

+3
source

Start by looking at a much simpler base shell: Tag, Length, Value (TLV). Your basic package will look like this:

 [Tag] [Length] [Value] 

A tag is a package identifier (for example, your action identifier).

Length - the length of the packet. You may need this to find out if you have a complete package. It will also let you figure out how much time matters.

The value contains actual data. The format of this can be any.

In the above case, these values ​​contain an additional series of TLV structures (parameter type, length, value). In fact, you do not need to send the number of parameters, since you can work with it along the length of the data and walking through the data.

As others said, I first put the package identifier (tag). If you have no problems with the cross platform, I would consider moving your serialized object to a TLV and submitting it through the wiring. If you make a mistake or want to change later, you can always create a new tag with a different structure.

See Wikipedia for more details on TLV .

+3
source

To avoid wheel reuse, any serialization protocol will run on wired data (such as XML, JSON), and you might consider BEEP for a basic protocol structure.

BEEP is well summarized in the FAQ document as a “sort of album of the“ best hits ”tricks used by experienced application protocol developers since the early 80s.

+3
source

There is no reason to make something so complicated. I see that you have an action identifier, so I assume that a certain number of actions will be committed.

For each action, you must define a data structure, and then you put each of these values ​​in the structure. To send it by cable, you simply allocate sum (size.i) bytes for each element of your structure. So your package will look like this:

 [action ID][item 1 (sizeof(item 1 bytes)][item 1 (sizeof(item 2 bytes)]...[item n (sizeof(item n bytes)] 

The idea is that you already know the size and type of each variable on each side of the connection, so you do not need to send this information.

For strings, you can simply throw them into a form with zero completion, and then when you “know” to search for a string based on your package type, start reading and look for zero.

-

Another option is to use '\ r \ n' to define your variables. This will require some overhead, and you will have to use text rather than binary values ​​for numbers. But that way you could just use readline to read each variable. Your packages will look like this:

 [action ID] [item 1 (as text)] ... [item n (as text)] 

-

Finally, just serializing objects and passing them by wire is a good way to do this too, with the least amount of code to write. Remember that you do not want to optimize prematurely as well as network traffic. If it turns out that you need to squeeze a little more performance later, you can go back and find out a more efficient mechanism.

And check google protocol buffers , which are supposedly an extremely fast way to serialize data in a neutral way, sort of like binary XML, but without nested elements. There is also JSON , which is another neutral coding platform. Using protocol buffers or JSON would mean you don't have to worry about how to specifically encode messages.

+2
source

Do you want the server to support multiple clients written in different languages? If not, then probably there is no need to specify the structure exactly; instead, use any means to serialize the data that your language offers, simply to reduce the likelihood of errors.

If you need a structure that is portable, then this looks fine, although in this case you should specify things like endianness and text encoding.

0
source

All Articles