Sessions
Session lifecycle and management
Sessions
Reference for OctoMY™'s communication sessions including lifecycle, states, and management.
Pro Tip
Session statistics (
rtt,packetLoss,packetsReceived) are invaluable for debugging connectivity issues. High RTT or packet loss often indicates network congestion or NAT timeout issues - consider reducing the keepalive interval if you see frequent disconnects.
Session overview
A CommsSession represents an active communication channel between two nodes:
Session states
State diagram
State descriptions
| State | Description |
|---|---|
| Closed | No active session |
| Connecting | Initiating connection |
| SYN_SENT | SYN sent, waiting for SYN |
| Handshaking | Key exchange in progress |
| Established | Active, encrypted communication |
| Closing | Graceful shutdown in progress |
Session lifecycle
Connection establishment
// Session creation flow
CommsSession* session = channel->createSession(associateId);
// State transitions
session->initiate(); // Closed -> Connecting
session->onSynSent(); // Connecting -> SYN_SENT
session->onSynAckReceived(); // SYN_SENT -> Handshaking
session->onAckConfirmed(); // Handshaking -> Established
Active session
// Send data
session->send(courierId, payload);
// Receive data (via signal)
connect(session, &CommsSession::dataReceived,
[](quint8 courierId, QByteArray data) {
// Handle incoming data
});
// Check state
if (session->isEstablished()) {
// Safe to send
}
Session termination
// Graceful close
session->close(); // Established -> Closing
// Immediate close
session->abort(); // Any state -> Closed
// Timeout handling
session->onTimeout(); // Various -> Closed
Session properties
Identity
| Property | Type | Description |
|---|---|---|
sessionId |
quint64 |
Unique session identifier |
localId |
quint32 |
Local node's session ID part |
remoteId |
quint32 |
Remote node's session ID part |
associateId |
QString |
Remote Associate ID |
Connection info
| Property | Type | Description |
|---|---|---|
remoteAddress |
CarrierAddress |
Peer's network address |
localAddress |
CarrierAddress |
Local bound address |
carrier |
CommsCarrier* |
Transport layer carrier |
Security
| Property | Type | Description |
|---|---|---|
sessionKey |
QByteArray |
AES-256 encryption key |
localNonce |
QByteArray |
Our handshake nonce |
remoteNonce |
QByteArray |
Peer's handshake nonce |
isEncrypted |
bool |
Encryption active |
Statistics
| Property | Type | Description |
|---|---|---|
rtt |
quint32 |
Round-trip time (ms) |
packetsReceived |
quint64 |
Total packets in |
packetsSent |
quint64 |
Total packets out |
bytesReceived |
quint64 |
Total bytes in |
bytesSent |
quint64 |
Total bytes out |
packetLoss |
float |
Loss percentage |
Session directory
CommsSessionDirectory
Manages all active sessions:
class CommsSessionDirectory : public QObject {
public:
// Session lookup
CommsSession* sessionByAssociateId(const QString& id);
CommsSession* sessionByAddress(const CarrierAddress& addr);
CommsSession* sessionById(quint64 sessionId);
// Session creation
CommsSession* createSession(const QString& associateId);
// Session enumeration
QList<CommsSession*> allSessions();
QList<CommsSession*> establishedSessions();
// Session removal
void removeSession(CommsSession* session);
signals:
void sessionAdded(CommsSession* session);
void sessionRemoved(CommsSession* session);
void sessionStateChanged(CommsSession* session, SessionState state);
};
Session lookup
// Find session by Associate ID
auto session = directory->sessionByAssociateId("abc123...");
// Find session by network address
CarrierAddressUDP addr("192.168.1.100", 8124);
auto session = directory->sessionByAddress(addr);
// Iterate all sessions
for (auto session : directory->establishedSessions()) {
qDebug() << session->remoteAddress();
}
Handshake details
Handshake state machine
Handshake data
struct HandshakeData {
quint16 protocolVersion;
quint8 nodeType; // Agent/Remote/Hub
quint16 capabilities; // Feature flags
QByteArray nonce; // 32-byte random
QByteArray publicKey; // RSA-2048 public key
QByteArray personalityId; // 64-byte identity hash
};
Key derivation
// After handshake completes:
QByteArray sharedSecret = deriveSharedSecret(
localPrivateKey,
remotePublicKey
);
QByteArray sessionKey = hkdf(
sharedSecret,
localNonce + remoteNonce,
"OctoMY Session Key",
32 // AES-256 key length
);
Reliability layer
Per-session reliability
Each session maintains reliability state:
struct ReliabilityState {
quint16 nextSendSeq; // Next sequence to send
quint16 nextExpectedSeq; // Next expected from peer
quint16 lastAckedSeq; // Last ACK sent
quint32 windowSize; // Current window size
// Pending reliable packets
QMap<quint16, PendingPacket> pendingPackets;
// Receive buffer for reordering
QMap<quint16, ReceivedPacket> receiveBuffer;
};
Packet tracking
struct PendingPacket {
quint16 sequence;
QByteArray data;
quint64 sentTime;
quint8 retryCount;
quint32 retryTimeout;
};
Acknowledgment handling
void CommsSession::onAckReceived(quint16 ackNum) {
// Remove acknowledged packets
auto it = mPendingPackets.begin();
while (it != mPendingPackets.end()) {
if (sequenceLessThan(it.key(), ackNum)) {
// Update RTT estimate
updateRTT(now() - it.value().sentTime);
it = mPendingPackets.erase(it);
} else {
++it;
}
}
}
Session events
Signals
class CommsSession : public QObject {
signals:
// State changes
void stateChanged(SessionState newState);
void established();
void closed();
// Data
void dataReceived(quint8 courierId, const QByteArray& data);
// Reliability
void packetAcknowledged(quint16 sequence);
void packetLost(quint16 sequence);
// Statistics
void rttUpdated(quint32 rttMs);
void statsUpdated();
};
Usage example
connect(session, &CommsSession::stateChanged,
[](SessionState state) {
switch (state) {
case SessionState::Established:
qDebug() << "Connection established!";
break;
case SessionState::Closed:
qDebug() << "Connection closed";
break;
}
});
connect(session, &CommsSession::dataReceived,
[](quint8 courierId, const QByteArray& data) {
handleCourierData(courierId, data);
});
Session timeouts
Timeout configuration
| Parameter | Default | Description |
|---|---|---|
| Connect timeout | 10s | Handshake time limit |
| Idle timeout | 60s | Max time without data |
| Keepalive interval | 5s | Ping frequency |
| Keepalive timeout | 15s | Disconnect after no pong |
Timeout handling
void CommsSession::checkTimeouts() {
quint64 now = currentTimeMs();
// Handshake timeout
if (mState == SessionState::Connecting ||
mState == SessionState::Handshaking) {
if (now - mStateEnteredTime > mConnectTimeout) {
emit connectTimeout();
close();
return;
}
}
// Idle timeout
if (mState == SessionState::Established) {
if (now - mLastActivityTime > mIdleTimeout) {
emit idleTimeout();
close();
return;
}
}
// Keepalive
if (now - mLastPingSent > mKeepaliveInterval) {
sendPing();
}
}
Session security
Encryption
All established sessions use AES-256-GCM:
QByteArray CommsSession::encrypt(const QByteArray& plaintext) {
QByteArray iv = generateRandomIV(16);
QByteArray ciphertext;
QByteArray tag;
aes256GcmEncrypt(
mSessionKey,
iv,
plaintext,
ciphertext,
tag
);
return iv + ciphertext + tag;
}
QByteArray CommsSession::decrypt(const QByteArray& encrypted) {
QByteArray iv = encrypted.left(16);
QByteArray tag = encrypted.right(16);
QByteArray ciphertext = encrypted.mid(16, encrypted.size() - 32);
QByteArray plaintext;
if (!aes256GcmDecrypt(mSessionKey, iv, ciphertext, tag, plaintext)) {
throw DecryptionError();
}
return plaintext;
}
Session key rotation
void CommsSession::rotateKey() {
// Derive new key from current key
QByteArray newKey = hkdf(
mSessionKey,
mLocalNonce + mRemoteNonce + QByteArray::number(mKeyRotation),
"OctoMY Key Rotation",
32
);
// Send key rotation notification
sendKeyRotation(mKeyRotation + 1);
// Update key
mSessionKey = newKey;
mKeyRotation++;
}
Multiple sessions
Session per peer
Session limits
| Setting | Default | Description |
|---|---|---|
| Max sessions | 100 | Per channel limit |
| Max pending | 10 | Incomplete handshakes |
| Session cleanup | 10s | Remove stale sessions |
Debugging sessions
Session status
void debugSession(CommsSession* session) {
qDebug() << "Session ID:" << session->sessionId();
qDebug() << "State:" << session->stateString();
qDebug() << "Remote:" << session->remoteAddress();
qDebug() << "RTT:" << session->rtt() << "ms";
qDebug() << "Packets in:" << session->packetsReceived();
qDebug() << "Packets out:" << session->packetsSent();
qDebug() << "Loss:" << session->packetLoss() << "%";
}
Common issues
| Issue | Cause | Solution |
|---|---|---|
| Stuck in Connecting | Firewall blocking | Open UDP port |
| Frequent disconnects | NAT timeout | Reduce keepalive |
| High packet loss | Network congestion | Reduce send rate |
| Decryption failures | Key mismatch | Re-pair nodes |