TUTORIAL: Stacking Protocols Part 2, Message Encapsulation

 

See Stacking Protocols Part 1.

MESSAGE ENCAPSULATION KEY CONCEPT: Each layer will only see data fields pertinent to its own layer

Let’s walk through an example. Suppose we’ve set up an echo client and server over PTCL. The stacks will look like this:

[ Echo Protocol (PTCL transport) ]       [ Echo Server (PTCL Transport)  ]
[ PTCL Protocol (C2C transport)  ]       [ PTCL Protocol (C2C Transport) ]
[ C2C Protocol (Wire transport)  ] <---> [ C2C Protocol (Wire transport) ]

As a reminder, the transport of each layer is set by the lower layer. Hence, Echo Protocol has a PTCL transport.

So, suppose the Echo Protocol sends an EchoProtocol message. This would take the form of constructing an EchoProtocol message builder object, and passing it to the transport:  transport.writeMessage(echoMessageBuilder)

For this example, we’re going to assume that the handshake has already happened. So the PTCL transport needs to send the data to the other side. Remember that transport.writeMessage simply serializes the message and passes it to transport.write, so the PTCL transport receives raw bytes, not the EchoProtocol data structure itself. Those raw bytes are stored in a new PTCL message that is then pushed to the lower transport. It might look like this:

class MyTclTransport(StackingTransportMixin):
  # inherits def writeMessage(self, msg):
  # that simply calls self.write(msg.serialize())

  def write(self, msgBytes):
    # in the echo example, "msgBytes" is the serialized EchoProtocol message.
    # do some processing, figure out the sequence number, setup a timeout for acks, etc
    # ready to create the PTCL packet
    ptclMessageBuilder = MessageData.GetMessageBuilder(PTCLMessage)
    # fill in all the necessary fields
    ptclMessageBuilder["data"].setData(msgBytes)
    
    # the .transport here is the lower (C2C) transport
    # THIS IS NOT SET AUTOMATICALLY, IT SHOULD BE
    # CONFIGURED BY YOUR PROTOCOL
    self.transport.writeMessage(ptclMessageBuilder)

The key think to note is that msgBytes is a serialized message. We put those serialized bytes into a new (PTCL) message and then serialize that for the C2C layer. The C2C Layer will wrap it in yet another message before serializing it for the wire.

On the reverse side, the process goes in reverse. The C2C layer will deserialize its message. It will deserialize only the C2C message. At the C2C layer, it doesn’t even know about PTCL. But when it passes it up to the next higher layer (PTCL), that layer deserializes the message.

Similarly, PTCL will process its own packet and decide if it has data to pass up. It might look like this:

def __handlePTCLMessage(self, prot, msg):
  # msg is a PTCL Message Builder
  # one of its fields is 'data', which may contain data for the next protocol
  # another field might be type, where type is HANDSHAKE, DATA, TERMINATE
  msgObj = msg.data()
  if msgObj.type == "HANDSHAKE":
    # process, BUT DO NOT PASS UP
  elif msgObj.type == "TERMINATE":
    # close the connection or something
  elif msgObj.type == "DATA":
    # in our example, msgObj.data is a serialized echo protocol message
    # the higher layer will deserialize it
    self.getHigherLayer().dataReceived(msgObj.data)
    # send acknowledgement or do any other PTCL processing

Leave a Reply

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