a protocol for reliable notifications over a 1 bit fallible connection.
What
imagine you have two devices, a client and a server, connected in a peculiar way:
- the server cannot send messages to the client without the client asking for them
- there are two channels, a request on one channel can only be responded to on the same channel
- the first channel has infinite bandwith and is perfectly reliable, but each message is obscenely expensive.
- the second channel is free, but can only send messages with a single payload bit, and the message has a 50% chance of being dropped
You've just been tasked with building a layer on top of this so that the server can send messages to the client, what do you do?
[DISCLAIMER: read the errata section before you go out and implement this]
How
Informal
the client sends 1-bit tokens. when the server receives that token, if it has generated new messages since it last sent that token, it responds with the boolean negation of that token.
the client always sends the last token it received. when it receives a token that is different from the one it sent, it retrieves a message from the side channel.
Formal
When the system start up, the devices initialize themselves to the following state:
1. the client sets it's client_token
variable to false
.
2. the server has an empty message queue
3. the server sets its server_token
to false, and new_messages
to false.
the client periodically sends its client_token
value along the unreliable channel.
when the server receives client_token
, it performs the following operation:
if client_token = server_token then
set new_messages false
end if
it then responds with server_token
.
whenever the server generates a new message, it performs the following operation:
if not new_messages then
set server_token (not server_token)
set new_messages true
end if
when the client receives server_token
, it performs the following operation:
if server_token ≠ client_token then
pull messages
set client_token server_token
Why
turns out this absurd hypothetical isn't actually that far off of how TCP+TLS and UDP behave, especially in the context of mobile and embedded devices.
this system also has the nice property that the 1-bit messages don't depend on the side-channel messages, useful if there are actually multiple clients, and perhaps multiple secure servers the client wants to be able to receive messages from, and the presence of new messages is multiplexed through a single unsecured server.
Errata 2024-10-25: one bit isn't enough, but two is
as shown here, using a single bit means the server can't tell the difference between a client sending the previous token and the next token.
however, if you replace the 1-bit bools with 2-bit unsigned integers, and replace the negation with a wrapping increment, the protocol works as intended.
additionally, i should have added a timing requirement to the infallible channel, otherwise long polling is a simpler and equally valid solution to the posed problem.