Sending Data Packets

You've already written some code for sending a simple data packet in the peer-to-peer session, but all you really did was send a single byte of data, and it didn't even really matter what that data was. In a real networking session, the traffic that will be sent between the server and the clients will be much more varied, and intended to perform specific actions.

To model more accurately what a "real" application might do, you should add a few options to the client that facilitate sending various data packets to the server. You can include the following actions for your client:

  • Check the number of players currently in the session.

  • Wave to other players in the session.

  • Send "private" data to the server.

  • Run away from the other players in the session.

  • Change the client's name.

Obviously, both the client and server applications will need to know about these actions, so you will add the following enumeration to the shared code file that will track the type of data packet you will be sending to the server:

 public enum NetworkMessages {     CheckPlayers,     Wave,     SendData,     RunAway,     ChangeName, } 

You have one value for each of the actions you want the client to perform. You could have just as easily created a list of constants to use, but the grouping of the enumeration made it more attractive for this simple case. Now you will need a way for the client to actually perform these actions. In your client application, create five buttons at the top of your form, one for each of these actions. In order to simplify the sending of the data, you will hook and create a single event handler for all of the buttons. The event handler method can be found in Listing 19.5.

Listing 19.5 Sending Data Based on Button Clicks
 private void button_Click(object sender, System.EventArgs e) {     // We want to send some data to the server     NetworkPacket packet = new NetworkPacket();     // Write the correct message depending on the button pressed     switch(((Button)sender).Name)     {         case "button1":             packet.Write(NetworkMessages.CheckPlayers);             break;         case "button2":             packet.Write(NetworkMessages.Wave);             break;         case "button3":             packet.Write(NetworkMessages.SendData);             break;         case "button4":             packet.Write(NetworkMessages.RunAway);             break;         case "button5":             if (textBox1.Text.Length > 0)             {                 packet.Write(NetworkMessages.ChangeName);                 packet.Write(textBox1.Text);                 PlayerInformation info = new PlayerInformation();                 info.Name = textBox1.Text;                 connection.SetClientInformation(info,                                   SyncFlags.ClientInformation);             }             else             {                 // Don't try to do anything if there is no name                 return;             }             break;     }     connection.Send(packet, 0, SendFlags.Guaranteed); } 

The first thing done here is to create a new network packet that will hold the data you want to send. Then you do a switch statement on the button's name. Unfortunately, C# doesn't currently have the ability to do a switch statement on a reference type such as the button itself, so this is the next best thing. You could have also done a series of if statements comparing the sender with each button, as well.

Depending on the name of the button pressed, you then write the appropriate message into the network packet. If the message happens to be the change name message, and there is a string in the text box, you not only write the correct message into the packet, you also write the new name and update your client information.

Finally, you send the data to the server. You will notice that there is no user id parameter to send the data to a particular user. In a client/server session, the client can only send data to the server; it isn't possible to send data to any other client directly.

With the client capable of sending data now, it would probably be a good idea to update the server so that it actually knows what to do with the data it's receiving. Naturally, before you can receive any of the data, you will need to hook the event that is fired. Add the following line to your initialize server method:

 connection.Receive += new     ReceiveEventHandler(OnDataReceive); 

Now, what you want to accomplish in this event handler is pretty simple. You want to detect the type of message being sent and react accordingly. You can do this with the handler found in Listing 19.6.

Listing 19.6 Handling Received Data for the Server
 private void OnDataReceive(object sender, ReceiveEventArgs e) {     NetworkMessages msg = (NetworkMessages)e.Message.ReceiveData.Read         (typeof(NetworkMessages));     NetworkPacket returnedPacket = new NetworkPacket();     string newtext = string.Empty;     switch (msg)     {         case NetworkMessages.ChangeName:             string newname = e.Message.ReceiveData.ReadString();             newtext = string.Format                 ("DPlay UserId 0x{0} changed name to {1}",                 e.Message.SenderID.ToString("x"), newname);             // The user wants inform everyone they ran away             returnedPacket.Write(NetworkMessages.ChangeName);             returnedPacket.Write(e.Message.SenderID);             returnedPacket.Write(newname);             // Now send it everyone             connection.SendTo((int)PlayerID.AllPlayers, returnedPacket, 0,                 SendFlags.Guaranteed | SendFlags.NoLoopback);             break;         case NetworkMessages.CheckPlayers:             newtext = string.Format                 ("Received CheckPlayers from DPlay UserId: 0x{0}",                 e.Message.SenderID.ToString("x"));             // The user wants to know how many players are in the session             returnedPacket.Write(NetworkMessages.CheckPlayers);             // subtract one user for the server user             returnedPacket.Write(connection.Players.Count - 1);             // Now send it only to that player             connection.SendTo(e.Message.SenderID, returnedPacket, 0,                 SendFlags.Guaranteed);             break;         case NetworkMessages.RunAway:             newtext = string.Format                 ("Received RunAway from DPlay UserId: 0x{0}",                 e.Message.SenderID.ToString("x"));             // The user wants inform everyone they ran away             returnedPacket.Write(NetworkMessages.RunAway);             returnedPacket.Write(e.Message.SenderID);             // Now send it everyone             connection.SendTo((int)PlayerID.AllPlayers, returnedPacket, 0,                 SendFlags.Guaranteed | SendFlags.NoLoopback);             break;         case NetworkMessages.SendData:             newtext = string.Format                 ("Received SendData from DPlay UserId: 0x{0}",                 e.Message.SenderID.ToString("x"));             // No need to reply, 'fake' server data             break;         case NetworkMessages.Wave:             newtext = string.Format                 ("Received Wave from DPlay UserId: 0x{0}",                 e.Message.SenderID.ToString("x"));             // The user wants inform everyone they waved             returnedPacket.Write(NetworkMessages.Wave);             returnedPacket.Write(e.Message.SenderID);             // Now send it everyone             connection.SendTo((int)PlayerID.AllPlayers, returnedPacket, 0,                 SendFlags.Guaranteed | SendFlags.NoLoopback);             break;     }     // We received some data, update our UI     this.BeginInvoke(new AddTextCallback(AddText),         new object[] { newtext }); } 

This method is definitely not as complex as it may look. In fact, it's actually quite simple. One of the properties of the event arguments is the network packet that was received. Much like you called the Write method to put data into the packet, you will call the Read method to extract that same data out.

READING DATA IN THE CORRECT ORDER

Note that you must always read the data in the exact same order you wrote it into the packet. Trying to read data in any other order will most likely cause errors in your application.

The read method takes a single type member that will be the type of data that is returned by the method. Since you want to first determine the type of message that was sent, you call read using the type of the enumeration. You also know that in certain cases, you will want to reply either to a specific client or to all clients with a "new" network packet, so go ahead and create that now. Now, you just need to use a switch statement on the received message type and perform the action it requests.

The easiest action is the SendData member. Since this is intended to just be a "private" message to the server, no response is needed, so you can simply update the server's UI to reflect that the message was received.

The Wave and RunAway actions are both virtually identical to each other. When either of these messages are received, you first update the server UI like normal; however, after that you begin filling the new network packet. You first write the exact same message you just received into it, followed by the user id of the player who sent the message originally. You do this so that when you send this message back to the clients, they know which client performed the action described. Finally, you send this packet back to all clients in the session, for them to react as they see fit. Notice that you include the NoLoopBack flag for this send. Without this flag, sending the messages would cause the server to get into an infinite loop (since each handling of a particular message results in that same message being sent again).

The CheckPlayers action is different from the rest. This is the client requesting information from the server, so no one other than the client needs to see the response from the sever. Once again you write the message id into the new network packet; however, next you write the number of players currently in the session (the one "phantom" server player is removed). You then send the new packet directly to the sender of the request, so no other players in the session can see this message from the server (unless they too request it). You also make sure you update the UI for the server.

The last action is most similar to the wave and run away actions. You'll notice a new method you call on the network packet here as well. In retrieving the new name of the client, you call the ReadString method. Assuming you used the write method to put the string into the network packet, this method should be able to extract the string directly from the network packet. The only major difference between this action and the wave and run away actions is that after writing the message id and the sender id, you also write the new name of the client in the packet as well.



Managed DirectX 9 Graphics and Game Programming, Kick Start
Managed DirectX 9 Kick Start: Graphics and Game Programming
ISBN: B003D7JUW6
EAN: N/A
Year: 2002
Pages: 180
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net