Multi-client, asynchronous sockets in C #, best practices?

I'm trying to better understand tcp / ip sockets in C #, because I want to challenge myself to see if I can create a working MMO infrastructure (game world, map, players, etc.) for educational purposes only, I'm not going to being one of those, "OMGZ is going to make my MMORPG r0x0r, which will be better than WoW !!!", you know what they are talking about.

In any case, I was wondering if anyone could shed some light on how to approach the development of this type of system and what things are needed, and what should I keep track of?

My initial idea was to split the system into separate client-server connections with each connection (on its own port) that performs a specific task, for example, updating player / monster positions, sending and receiving chat messages, etc., which I would facilitate the processing of data because you did not always have to place the data header to know what information the package contains.

Does this make sense and is it helpful, or am I just complicating things?

Your feedback is greatly appreciated.

+17
c # client-server sockets
Nov 12 '08 at 18:37
source share
4 answers

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:

+30
Nov 13 '08 at 1:04
source share

I think you need to crawl before you go and before you run. First create a framework and connection structure, and then about scalability. If your goal is to learn programming in C # and tcp / ip, then do not do it more difficult for yourself.

Continue your initial thoughts and keep sharing data streams.

have fun and good luck

+2
Nov 12 '08 at 20:13
source share

I would advise against using multiple connections for various information. I would develop some protocol that contains a header with the information to be processed. Use as few resources as possible. In addition, you may not want to send updates for different positions and statistics from the client to the server. Otherwise, you may be in a situation where the user may change his data sent back to the server.

Say the user is faking the location of the monster in order to pass something. I would only update the vector of user position and action. Let the rest of the information be processed and verified by the server.

+1
Nov 12 '08 at 18:51
source share

Yes, I think that you are right about a single connection, and, of course, the client will not send any actual data to the server, more like simple commands such as "move forward", "turn left", etc. , and the server will move the character to the map and send the new coordinates back to the client.

0
Nov 12 '08 at 18:56
source share



All Articles