If you are programming at the socket level, then no matter how many ports you open for each type of message, you still need to have some kind of header. Even if it's just the length of the rest of the message. Having said that, it's easy to add a simple header and tail structure to the message. I would think that it is easier to deal with only one port on the client side.
I believe that modern MMORPGs (and possibly even the old ones) had two levels of servers. Login servers that verify you as a billing client. After checking, they transfer you to the game server, which contains all the information about the game world. However, this still requires the client to have one socket open, but not forbid to have more.
In addition, most MMORPGS also encrypt all of their data. If you write this as an exercise for pleasure, then it will not make much difference.
For developing / writing protocols in general, here is what I am worried about:
byte order
Whether the client and server are always guaranteed to have the same content. If not, I need to handle this in my serialization code. There are several ways to handle endianess.
- Ignore it - Obviously a bad choice
- Indicate protocol compliance. This is what the old protocols did / did, therefore, the term "network order", which has always been a big argument. In fact, it does not matter which concretization you indicate only that you indicate one or another. Some crunchy old network programmers will come to hand if you are not very enthusiastic, but if your servers and most clients are not very oriented, you really do not buy yourself anything other than extra work, making the protocol a big endian.
- Mark Endianess in each heading. You can add a cookie that will tell you the ultimate goal of the client / server and give each message a conversion accordingly as needed. Extra work!
- Make your protocol inactive - if you send everything as ASCII strings then endianess doesn't matter, but is also much more inefficient.
Of 4, I would usually choose 2 and indicate that endianess will be that of most customers, who will now be few in number.
Backward and Backward Compatibility
Should the protocol be forward and reverse. The answer should always be yes. In this case, it will determine how I design the entire protocol from the point of view of version control and how each individual message is created to handle minor changes that should not actually be part of the version control process. You can use this and use XML, but you are losing a lot of efficiency.
For general version control, I usually design something simple. The client sends a message with the version indicating that it says the XY version, if the server can support this version, it sends back a message confirming the version of the client, and everything goes forward. Otherwise, it deletes the client and terminates the connection.
For each post, you have something like the following:
+-------------------------+-------------------+-----------------+------------------------+ | Length of Msg (4 bytes) | MsgType (2 bytes) | Flags (4 bytes) | Msg (length - 6 bytes) | +-------------------------+-------------------+-----------------+------------------------+
The length obviously tells you how long the message takes, not counting the length itself. The message type is MsgType. There are only two bytes for this, since 65356 is a multitude of message types for applications. Flags so you know what is serialized in the message. This field combined with length is what gives you forward and backward compatibility.
const uint32_t FLAG_0 = (1 << 0); const uint32_t FLAG_1 = (1 << 1); const uint32_t FLAG_2 = (1 << 2); ... const uint32_t RESERVED_32 = (1 << 31);
Then your deserialization code might do something like the following:
uint32 length = MessageBuffer.ReadUint32(); uint32 start = MessageBuffer.CurrentOffset(); uint16 msgType = MessageBuffer.ReadUint16(); uint32 flags = MessageBuffer.ReadUint32(); if (flags & FLAG_0) { // Read out whatever FLAG_0 represents. // Single or multiple fields } // ... // read out the other flags // ... MessageBuffer.AdvanceToOffset(start + length);
This allows you to add new fields to the end of messages without having to rethink the entire protocol. It also ensures that older servers and clients will ignore flags that they are not aware of. If they should use new flags and fields, then you simply change the general version of the protocol.
Use frame work or not
There are various network structures that I would consider for use in a business application. If I didn’t have any special problems, I would go with a standard structure. In your case, you want to learn socket-level programming, so this is a question for you.
If you use the framework, make sure that it addresses the two problems above or at least does not bother you if you need to configure it in these areas.
I deal with a third party
In many cases, you can deal with a third-party server / client with which you need to communicate. This implies several scenarios:
- They already have a protocol installed - just use your protocol.
- You already have a protocol defined (and they are ready to use it) - again, just use a specific protocol
- They use a standard platform (based on WSDL, etc.). Use the framework.
- Neither party has a specific protocol. Try to determine the optimal solution based on all factors (all those that I mentioned here), as well as their level of competence (at least as much as possible). Regardless, make sure both parties agree and understand the protocol. From experience, this can be painful or enjoyable. It depends on who you work with.
In any case, you will not work with a third party, so this is really just added for completeness.
It seems to me that I could write much more about this, but it is quite long. I hope this helps, and if you have any specific questions, just ask about Stackoverflow.
Editing the answer to the knoopx question: