Author: eilemann@gmail.com
State: Implemented in 0.1
Background
The packet handling code in Equalizer is the core piece of the networking
layer. This document describes the packet dispatch mechanism used by the
Equalizer network layer (namespace eq::net
). The implementation
matches this document at the time of writing (Wed Dec 23 11:47:10 2015 +0100), but may diverge over
time.
Packets
Packet
s are the base piece of information exchanged
between Node
s. A packet has a data type to identify the object it
is addressed to, and a command number to identify the purpose of the
packet. Packets contain additional data depending on the data type and
command, as explained below.
Nodes
Node
s are the basic entity in the network layer. They communicate
with each other using Connection
s. During initialization, a node
typically opens a listening connection, and starts the receiver thread. The
receiver thread listens on all connections of the node. When a new node
connects, he calls handleConnect
, which accepts the connection,
creates a new Node and adds this connected node to the set of
connections. Nodes send each other Packet
s. The receiving node
reads the packet from the connection and calls dispatchCommand
.
Commands
Packets read by the node are wrapped in a Command
, which holds
the packet and node. The Command
manages the ownership of the
packet and provides convenience functions to access the packet. The ownership
management is a lightweight mechanism similar to reference counting, except
that the allocated packet can be transferred from one command to another,
which is used when the packets are not handled immediately (see redispatch and
push cases below). This mechanism allows reuse of handled commands, and avoids
copies when the command will be handled later.
The eq::net Base Class
co::Dispatcher
provides packet dispatching using a command
handler table. During construction, sub classes provide command handlers for
each command using registerCommand
. The method
invokeCommand
calls the registered command handler for the
provided packet. The command handler may return one of the following command
result codes, which is returned by invokeCommand
:
- COMMAND_HANDLED
- The command was handled correctly.
- COMMAND_REDISPATCH
- The command can not be handled at the moment, and should be redispatched later.
- COMMAND_PUSH
- The command can not be handled from the command handler, and should be pushed to another entity, typically to another thread.
- COMMAND_PUSH_FRONT
- Like COMMAND_PUSH, except that the command has a high priority, e.g., it bypasses other commands queued for execution by another thread.
- COMMAND_ERROR
- A critical error occured during command processing.
Node Packet Dispatch
Depending on the packet's data type, dispatchPacket
decides how
the packet is handled. For node packets, invokeCommand
is
called. The result is handled accordingly, that is, COMMAND_REDISPATCH causes
the packet to be added to the list of redispatch packets, and for COMMAND_PUSH
and COMMAND_PUSH_FRONT pushCommand
or
pushCommandFront
is called, respectively. For
Session
and Object
packets,
Session::dispatchPacket
is invoked. The session is identified by
the sessionID contained in the SessionPacket
. For all other data
types, handleCommand
is called.
Note: Any command handler called directly or indirectly
from dispatchPacket
is not allowed to block, since it is executed
from within the receiver thread and would therefore block the packet handling.
Session Packet Dispatch
The Session
inherits, like the node, from
co::Dispatcher
. The session dispatch works similar to the node
dispatch. For session packets, invokeCommand
is called, and the
result is returned to the node, which handles it accordingly. For object
packets, Object::invokeCommand
is called and the result is
returned. The object is retrieved using the objectID, and possibly instanceID,
contained in the ObjectPacket
.
Asynchronous Handling of Packets
Several commands for Equalizer entities, for example window initialisation
requests, have to be handled by another thread. A
thread-safe CommandQueue
is used to transfer the packet from the
receiver to the consumer thread. This is either done by a special command
handler, or by overriding Node::pushCommand
or Node::pushCommandFront
, depending on the object, command and
destination thread.