This document is about: REALTIME 5
SWITCH TO

Traffic Stats

Stats

Every PhotonPeer counts what it sends and receives while connected. In Realtime, that peer is RealtimeClient.RealtimePeer, so the counters are at:

C#

TrafficStats stats = client.RealtimePeer.Stats;   // Photon.Client.TrafficStats

Nothing has to be enabled: the stats always run (v4's TrafficStatsEnabled is obsolete and without function). Stats is replaced with a fresh instance on every Connect(), so all values are per-connection and only meaningful while connected.

Before digging into numbers, raise the log level — most connection and matchmaking problems are already spelled out in the text log (see Logging). The client also logs a stats one-liner for you: with LogLevel.Info, RealtimeClient.LogStatsInterval (default 5000 ms) logs RealtimePeer.VitalStatsToString(false) in that interval.

C#

client.RealtimePeer.VitalStatsToString(false);
// Stats duration: 12.00 sec. rtt(var): 34(3)ms.  120 kB -> 10 kB/sec.

client.RealtimePeer.VitalStatsToString(true);   // same line + the full TrafficStats breakdown

Two related values are on the peer itself, not in Stats: QueuedIncomingCommands and QueuedOutgoingCommands. Growing queues point at a dispatch/send loop that is not called often enough.

Traffic Stats

TrafficStats counters accumulate for the lifetime of the connection, so a single reading tells you little. The useful pattern is snapshot → delta:

  • TrafficStats.ToSnapshot() returns a TrafficStatsSnapshot: all counters plus SnapshotTimestamp (ms since Connect()).
  • TrafficStats.ToDelta(reference) returns a TrafficStatsDeltanow - reference, including DeltaTime in milliseconds.

C#

private TrafficStatsSnapshot reference;

void LogTraffic()   // call in some interval
{
    if (!client.IsConnected)
    {
        this.reference = null;      // drop stale reference; Stats is new per connection
        return;
    }

    TrafficStats stats = client.RealtimePeer.Stats;
    if (this.reference == null)
    {
        this.reference = stats.ToSnapshot();
        return;
    }

    TrafficStatsDelta delta = stats.ToDelta(this.reference);
    this.reference = stats.ToSnapshot();     // this "now" is the next interval's reference

    float seconds = delta.DeltaTime / 1000f;
    int inPerSec = seconds > 0f ? (int)(delta.BytesIn / seconds) : 0;
    int outPerSec = seconds > 0f ? (int)(delta.BytesOut / seconds) : 0;
    Log.Info($"in {inPerSec} B/s, out {outPerSec} B/s, rtt {stats.RoundtripTime} ms");
}

TrafficStatsDelta.ToString(udpValues, rttValues, callValues) already formats bytes, rates, UDP commands, RTT and call counts, if you just want a log line. TrafficStats.ToString(extended) does the same for the absolute values. demo-realtime-console/GameLogic.cs (LogTrafficStats()) shows the whole loop.

Reading the values

Volume (TrafficStatsBase, so present in stats, snapshots and deltas):

Value Meaning
BytesIn / BytesOut Payload bytes, excluding transport-layer headers.
PackagesIn / PackagesOut Datagrams / packages.
UdpFragmentsIn / UdpFragmentsOut Commands split because they exceed the MTU. Steady fragments mean your messages are too big for one datagram.
UdpUnreliableCommandsSent Unreliable commands sent (total, all channels).
UdpReliableCommandsSent Reliable commands sent, excluding resends.
UdpReliableCommandsResent Reliable commands repeated because no ACK arrived in time — the client-side indicator for packet loss.
UdpReliableCommandsInFlight Sent but not yet acknowledged. A rising value means the connection is not keeping up.
DispatchIncomingCommandsCalls / SendOutgoingCommandsCalls How often you called those methods.

Timings (only on TrafficStats, the live instance):

Value Meaning
LongestDeltaBetweenSendOutgoingCalls Worst gap between SendOutgoingCommands() calls. Crucial: without sending, the server times out this client.
LongestDeltaBetweenDispatchCalls Worst gap between DispatchIncomingCommands() calls. Not fatal, but it adds local lag to events already received.
LastSendOutgoingDeltaTime, LastDispatchDeltaTime, LastReceiveDeltaTime, LastSendAckDeltaTime Milliseconds since the last send / dispatch / anything received / ACKs sent.
LastDispatchDuration Milliseconds spent in the last dispatch callback — long callbacks stall the loop.

ResetMaximumCounters() clears the two Longest… values (and the internal last-call timestamps), e.g. to measure one level or match at a time.

Notes:

  • UDP-specific values stay 0 on TCP / WebSocket connections.
  • The delta's RoundtripTime, RoundtripTimeVariance and LastRoundtripTime are differences between the two snapshots, not absolute values. For absolutes, use ReferenceRoundtripTime / LaterRoundtripTime on the delta, or read Stats.RoundtripTime directly.
  • Migrating from v4: the split TrafficStatsGameLevel / incoming / outgoing instances, TrafficStatsEnabled and TrafficStatsReset() are gone. Everything is in PhotonPeer.Stats, and resetting is replaced by keeping a snapshot.

Round-Trip Time

Stats.RoundtripTime is the time until a reliable command is acknowledged by the server, in milliseconds — a running estimate, not a single measurement. Every acknowledged reliable command contributes a fraction (Jacobson/Karels smoothing, starting at 200 ms on connect). On UDP it includes the server's configured ACK-delay; on TCP and WebSocket there is none, so values are slightly lower.

  • Stats.RoundtripTimeVariance is the jitter of that estimate. It also feeds the SDK's resend timing, so a high variance delays resends.
  • Stats.LastRoundtripTime is the single last measurement — spiky, useful for debugging, not for display.

Updates come from any reliable command on UDP; on TCP and WebSocket, from the automatic ping. PhotonPeer.PingInterval (default 1000 ms) controls how often pings are sent — on UDP, pings are skipped when a reliable command was sent in that interval anyway.

Practically: RoundtripTime is also the approximate time until a raised event reaches another client or an operation result comes back. Add half of it as expected one-way delay when tuning interpolation or input delay. Watch RoundtripTimeVariance and UdpReliableCommandsResent together — a stable 120 ms connection plays better than one averaging 60 ms with heavy jitter and resends.

The obsolete PhotonPeer.RoundTripTime, RoundTripTimeVariance and LastRoundTripTime properties still work but forward to Stats; use Stats in new code.

Back to top