TUTORIAL: Stacking Protocols Part 1

Hopefully, by this point, everyone of you has either written a Playground protocol or is on a team that has written a Playground protocol. But for those that haven’t, here’s a super simple reminder:

class MyProtocol(SimpleMessageHandlingProtocol):
  # in your init, you define which types of messages you will handle. 
  def __handleMyMessage(self, someMessage):
    # handle the received message here...
    # when you're ready to send,
    self.transport.writeMessage(responseMessage)

Ok, so data comes into your protocol from the framework and is handled by your handler. You write back to the network using “self.transport.write” or “self.transport.writeMessage”. Any problems with this so far?

Now let’s talk about stacked protocols.

What if you wanted to write a stack of protocols into your framework? Something similar to how TCP and IP are stacked. What you do is write *two* protocols and connect them together.

class MyTopProtocol(...):
  # protocol def

class MyBottomProtocol(...):
  # protocol def

How would you hook these two together? While there are a number of variations, these are the critical things you *have* to know.

  1. When data is received at the bottom protocol, it has to process it and then pass it “up” to the top protocol
  2. When data is sent from the top protocol, it has to be pushed “down” to the bottom protocol

If you’ve written a Playground Protocol, you haven’t realized it, but you’ve been using a protocol stack already. There’s already a C2C layer Playground Protocol that you connect to when you use the .listen() and .connect() methods. The C2C protocol receives data from the network, and then passes it “up” to your layer. Similarly, when you call transport.write(), that data is being pushed down to the C2C layer below.

But now, we need to stack a protocol (the PTCL reliable protocol) on top of C2C, but below any application layers. The diagram would look like this:

[Application Layer]
   |         | 
[   PTCL Layer    ]
   |         |
[   C2C Layer     ]

Your application layers did *not* have to support stacking, because there’s nothing above them. But your PTCL layer will.

The Playground framework provides some helper classes. You need to inherit from “StackingProtocolMixin” class in your protocol. The mixin doesn’t do much, but it it provides a “setHigherProtocol” and “getHigherProtocol” methods. You can use the getHigherProtocol() method to pass data up.

from playground.network.common import SimpleMessageHandlingProtocol, StackingProtocolMixin
class PTCLProtocol(SimpleMessageHandlingProtocol, StackingProtocolMixin):
  def __ptclMessageHandler(self, protocol msg):
    # process the msg
    # when we're ready to "pop" data up to the higher layer:
    self.getHigherProtocol().dataReceived(rawData)

You call the dataReceived method on the higher protocol. It is responsible to deserialize the data into a message.

The other thing that has to happen is you have to give the higher layer a transport. You can create a transport by inheriting fromnetwork.client.ClientApplicationTransport and overwriting the write() method. Data received from this write method gets encapsulated in your PTCL layer and sent down the network stack. You give the transport to the higher layer when you’re ready:

self.getHigherProtocol().makeConnection(myPtclTransport)

Generally, you don’t do this until the connection is open (e.g., after the three-way handshake).

But how does the higher protocol get set? When are the two connected?

The factories that produce the protocols also have to be stacked. When it produces a protocol for one, it has to produce the protocol for the higher and connect the two together.

[ TopFactory ] ------> [ TopProtocol]
     |                      |
[ BottomFactory ] ---> [ BottomProtocol ]

The BottomFactory builds the BottomProtocol, then tells the TopFactory to build the TopProtocol. It then calls BottomProtocol.setHigherProtocol(TopProtocol) to hook them together.

ClientApplicationServer and ClientApplicationClient are already stacking factories, so you don’t have to change your factories at all. They already supportsetHigherFactory(), which is required to make this work.

To plug into playground, you have to provide a helper function to the framework for sticking the factories together. It should look something like this:

class MyPTCLServer(ClientApplicationServer):
  Protocol = PTCLServerProtocol

class MyPTCLClient(ClientApplicationClient):
  Protocol = PTCLClientProtocol

def createPtclServerStack(higherFactory):
  ptclFactory = MyPTCLServer()
  ptclFactory.setHigherFactory(higherFactory)
  return ptclFactory

def createPtclClientStack(higherFactory):
  ptclFactory = MyPTCLClient()
  ptclFactory.setHigherFactory(higherFactory)
  return ptclFactory

Now that you have this, you can plug it into playground,conf:

=====connection_types=====
     list = RAW, RELIABLE_STREAM
======RAW======
      client = None
      server = None

======RELIABLE_STREAM======
      client = full.module.path.createPtclClientStack
      server = full.module.path.createPtclServerStack

Now, if in Playground, you were to call clientBase.listen(EchoServer, 101, "RELIABLE_STREAM"), the Echo server factory will get plugged into a stack of PTCL on top of C2C.

Leave a Reply

Your email address will not be published. Required fields are marked *