Sending and receiving custom datagrams

After having built the groundwork for opening network connections, it's time to implement our first self-defined network protocol. To achieve this goal, we are going to implement new classes derived from Protocol that will piece together custom datagrams. We will then send these datagrams back and forth between our client and server to have them a nice little chat.

Getting ready

This recipe directly continues where the last one left off. So if you didn't read that part yet, take one step back and start from the beginning to create the prerequisites for this recipe and better understand what will be shown here.

How to do it...

Follow these steps to implement your own network protocol:

  1. Open Application.py and add the following import statements to the top of the file:
    from direct.distributed.PyDatagram import PyDatagram
    from direct.distributed.PyDatagramIterator import PyDatagramIterator
    
  2. Add the following two methods to the Protocol class:
    def printMessage(self, title, msg):
    print "%s %s" % (title, msg)
    def buildReply(self, msgid, data):
    reply = PyDatagram()
    reply.addUint8(msgid)
    reply.addString(data)
    return reply
    
  3. Directly below the code of the Protocol class, add this:
    class ServerProtocol(Protocol):
    def process(self, data):
    it = PyDatagramIterator(data)
    msgid = it.getUint8()
    if msgid == 0:
    return self.handleHello(it)
    elif msgid == 1:
    return self.handleQuestion(it)
    elif msgid == 2:
    return self.handleBye(it)
    def handleHello(self, it):
    self.printMessage("Server received:", it.getString())
    return self.buildReply(0, "Hello, too!")
    def handleQuestion(self, it):
    self.printMessage("Server received:", it.getString())
    return self.buildReply(1, "I'm fine. How are you?")
    def handleBye(self, it):
    self.printMessage("Server received:", it.getString())
    return self.buildReply(2, "Bye!")
    
  4. Now add the ClientProtocol class below ServerProtocol:
    class ClientProtocol(Protocol):
    def process(self, data):
    it = PyDatagramIterator(data)
    msgid = it.getUint8()
    if msgid == 0:
    return self.handleHello(it)
    elif msgid == 1:
    return self.handleQuestion(it)
    elif msgid == 2:
    return self.handleBye(it)
    def handleHello(self, it):
    self.printMessage("Client received:", it.getString())
    return self.buildReply(1, "How are you?")
    def handleQuestion(self, it):
    self.printMessage("Client received:", it.getString())
    return self.buildReply(2, "I'm fine too. Gotta run! Bye!")
    def handleBye(self, it):
    self.printMessage("Client received:", it.getString())
    return None
    
  5. Modify the Application class to resemble the code below:
    class Application(ShowBase):
    def __init__(self):
    ShowBase.__init__(self)
    server = Server(ServerProtocol(), 9999)
    client = Client(ClientProtocol())
    client.connect("localhost", 9999, 3000)
    data = PyDatagram()
    data.addUint8(0)
    data.addString("Hello!")
    client.send(data)
    
  6. Start the program and watch the Output area. You should be able to observe the following output:
Client: Connected to server.
Server: New connection established.
Server received: Hello!
Client received: Hello, too!
Server received: How are you?
Client received: I'm fine. How are you?
Server received: I'm fine too. Gotta run! Bye!
Client received: Bye!

How it works...

As we can see in this sample, sending and receiving data over a network connection is really easy, thanks to the API provided by Panda3D. To send data, we need to create a new PyDatagram and add data fields using methods like addString() and addUint8() before passing it to a ConnectionWriter instance for sending. We are not limited to sending strings and 8 bit unsigned integers, though. PyDatagram features a whole lot of these add*() methods for floating point numbers and integers of various bit widths, for example.

To retrieve data that has been received over a network connection we have to pass it to a PyDatagramIterator. With the help of this class we are able to unpack the data fields from the datagram. Of course, this works quick and easy, but there's a catch to it that is very important to keep in mind: When retrieving data from a datagram using a PyDatagramIterator, the fields need to be accessed in exactly the same order as they were added to the PyDatagram before sending!

Using this knowledge, we were able to build a simple communication protocol that sends a numerical message id and a string. The receiver displays the string it got on the console and sends a reply containing a new message id and string based on the numerical id it received.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset