Photon and its clients are using a highly optimized binary protocol to communicate. It's compact, yet easy to parse. You don't have to know its details, as it is handled behind the scenes. Just in case you want to know it anyways, this page describes it.
The Photon binary protocols are organized in several layers. On its lowest level, UDP is used to carry the messages you want to send. Within those standard datagrams, several headers reflect the properties you expect of Photon: optional reliability, sequencing, aggregation of messages, time synchronization and various others.
The following chart shows the individual layers and their nesting:
In words: Any UDP package contains an eNet header and at least one eNet command. Each eNet command carries our messages: An operation, result or event. Those in turn consist of the operation header and the data you provided.
Below, you will find tables listing each part of the protocols and its size. This should give you an idea of the bandwidth needed by certain content you send. If you wanted to, you could calculate how big any given message becomes.
To save you this work, we have an example, too.
Example: Join "somegame"
Let's take a look at the operation Join (implemented by the Lite Application). Without properties, this is an operation with a single parameter.
The "operation parameters" require: 2 + count(parameter-keys) + size(parameter-values) bytes. The string "somegame" uses 8 bytes in UTF8 encoding and strings require another 3 bytes (length and type information). Sum: 14 bytes.
The parameters are wrapped into an operation-header, which is 8 bytes. Sum: 22 bytes.
The operation code is not encoded as parameter. Instead it's in the operation-header.
The operation and its header are wrapped into eNet's "send reliable" command as payload. The command is 12 bytes. Sum: 34 bytes.
Let us assume the command is sent when no other commands are queued. The eNet package header is 12 bytes. Sum: 46 bytes for the reliable, sequenced operation.
Last but not least, the eNet package is put into a UDP/IP datagram, which adds 28 bytes headers. Quite a bit, compared to the 46 bytes so far. Sadly, this can't be avoided completely but our aggregation of commands can save you a lot of bandwidth, sharing those headers.
The complete operation takes up 74 bytes.
The server will have to acknowledge this command. The ACK command is 20 bytes. If this is sent alone in a package, it will take up 40 bytes in return.
Enet Channels allow you to use several independent command-sequences. In a single channel, all commands are sent and dispatched in order. If a reliable command is missing, the receiving side can't continue to dispatch updates and things lag.
When some events (and operations) are independent from one-another, you can put them im separate channels. Example: In-room chat messages should be reliable but if they arrive late, the position updates should not be delayed by (temporarily) missing messages.
By default Photon has two channels and channel zero is the default to send operations. The operations join and leave are always sent in channel zero (for simplicity). There is a "background" channel 255 used internally for connect and disconnect messages. This is ignored for the channel count.
Channels are prioritized: the lowest channel number is put first. Data in a higher channel might be sent later when a UDP package is already full.
Operation Content - Serializable Types
You can find detailed table of Photon serializable types and their respective sizes at this link.
On the client side, operation parameters and their values are aggregated within a hashtable. As each parameter is resembled by a byte key, operation requests are streamlined and use less bytes than any "regular" hashtable. Results for operations and events are encoded in the same way as operation parameters. An operation result will always contain: opCode, returnCode and a debug string. Events always contain: evCode and the event data hashtable.
|| reliable, per connection
| verify connect
|| reliable, per connect
| init message
|| reliable, per connection (choses the application)
| init response
|| reliable, per init
|| reliable, called in intervals (if nothing else was reliable)
| fetch timestamp
|| a ping which is immediately answered
|| unreliable, per reliable command
|| reliable, might be unreliable in case of timeout
| send reliable
|| 12 + payload
|| reliable, carries a operation, response or event
| send unreliable
|| 16 + payload
|| unreliable, carries a operation, response or event
|| 32 + payload
|| reliable, used if the payload does not fit into a single datagram
UDP Packet Content - Headers
|| 28 + size(optional headers)
|| IP + UDP Header.
Optional headers are less than 40 bytes.
| enet packet header
|| contains up to byte.MaxValue commands
| operation header
|| in a command (reliable, unreliable or fragment)
contains operation parameters