FSM-Based Message Gating
LiteP2P uses a Finite State Machine (FSM) to control message flow. This ensures reliable, ordered communication that respects connection state.
What is Message Gating?
Message gating prevents messages from being sent when the connection is not in an appropriate state. This avoids:
- Lost messages sent to disconnected peers
- Security issues from data sent before handshake completes
- Protocol violations that cause connection drops
- Wasted battery on futile send attempts
FSM States
LiteP2P enforces strict state-based messaging:
| FSM State | Allowed Messages | Description |
|---|---|---|
CONNECTED |
Handshake only | TCP connected, handshake not started |
HANDSHAKING |
Handshake only | Cryptographic handshake in progress |
READY |
Application data | Fully authenticated, ready for data |
DEGRADED |
Control only | Connection unstable, limiting traffic |
DISCONNECTED |
None | Not connected |
FAILED |
None | Connection failed, awaiting cleanup |
State Diagram
┌─────────────────┐
│ DISCONNECTED │
└────────┬────────┘
│ connect()
▼
┌─────────────────┐
│ CONNECTED │
└────────┬────────┘
│ startHandshake()
▼
┌─────────────────┐
│ HANDSHAKING │
└────────┬────────┘
│ handshakeComplete()
▼
┌─────────────────┐
┌───────│ READY │───────┐
│ └─────────────────┘ │
│ networkDegraded() disconnect()│
▼ ▼
┌───────────────┐ ┌─────────────────┐
│ DEGRADED │ │ DISCONNECTED │
└───────┬───────┘ └─────────────────┘
│ recovered() or timeout
▼
┌───────────────┐
│ FAILED │
└───────────────┘
Message Types
LiteP2P categorizes messages for state-based filtering:
Handshake Messages
Only allowed in CONNECTED and HANDSHAKING states:
HELLO– Initial connection greetingKEY_EXCHANGE– Cryptographic key exchangeAUTH– Authentication challenge/responseHANDSHAKE_COMPLETE– Finalize handshake
Control Messages
Allowed in READY and DEGRADED states:
PING/PONG– Connection health checkCLOSE– Graceful disconnectERROR– Error notification
Application Messages
Only allowed in READY state:
DATA– Application data payloadFILE_TRANSFER– File transfer framesSTREAM– Streaming data
Working with FSM
val peer = LiteP2P.getInstance().getPeer(peerId)
// Check state before sending
if (peer.state == PeerState.READY) {
peer.send(data) { result ->
when (result) {
is SendResult.Success -> { /* sent */ }
is SendResult.Error -> { /* handle error */ }
}
}
} else {
// Queue for later or notify user
messageQueue.add(data)
}
// Listen for state changes
peer.onStateChange { newState ->
when (newState) {
PeerState.READY -> {
// Flush queued messages
messageQueue.forEach { peer.send(it) }
messageQueue.clear()
}
PeerState.DISCONNECTED -> {
// Store messages for later
}
}
}
Automatic Message Handling
LiteP2P can automatically queue and retry messages:
val config = PeerConfig.Builder()
.setAppId("your-app-id")
.setMessageQueueSize(100)
.setAutoRetry(true)
.setRetryOnReconnect(true)
.build()
// Messages sent to non-ready peers are automatically queued
LiteP2P.getInstance().sendMessage(peerId, data) { result ->
// Called when message is actually delivered (may be after reconnect)
}
Why FSM Matters
FSM-based messaging prevents common P2P bugs: race conditions during handshake, data leaks before encryption is established, and wasted resources on dead connections.