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