76 lines
2.6 KiB
Python
76 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
import queue
|
|
import time
|
|
from datetime import datetime, timedelta
|
|
from threading import Thread
|
|
|
|
from mixnet.mixnet import Mixnet, MixnetTopology
|
|
from mixnet.node import PacketQueue
|
|
from mixnet.packet import PacketBuilder
|
|
from mixnet.poisson import poisson_interval_sec
|
|
|
|
|
|
class MixClientRunner(Thread):
|
|
"""
|
|
Emit packets at the Poisson emission_rate_per_min.
|
|
|
|
If a real packet is scheduled to be sent, this thread sends the real packet to the mixnet,
|
|
and schedules redundant real packets to be emitted in the next turns.
|
|
|
|
If no real packet is not scheduled, this thread emits a cover packet according to the emission_rate_per_min.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
mixnet: Mixnet,
|
|
topology: MixnetTopology,
|
|
emission_rate_per_min: int, # Poisson rate parameter: lambda in the spec
|
|
redundancy: int, # b in the spec
|
|
real_packet_queue: PacketQueue,
|
|
outbound_socket: PacketQueue,
|
|
):
|
|
super().__init__()
|
|
self.mixnet = mixnet
|
|
self.topology = topology
|
|
self.emission_rate_per_min = emission_rate_per_min
|
|
self.redundancy = redundancy
|
|
self.real_packet_queue = real_packet_queue
|
|
self.redundant_real_packet_queue: PacketQueue = queue.Queue()
|
|
self.outbound_socket = outbound_socket
|
|
|
|
def run(self) -> None:
|
|
# Here in Python, this thread is implemented in synchronous manner.
|
|
# In the real implementation, consider implementing this in asynchronous if possible.
|
|
|
|
next_emission_ts = datetime.now() + timedelta(
|
|
seconds=poisson_interval_sec(self.emission_rate_per_min)
|
|
)
|
|
|
|
while True:
|
|
time.sleep(1 / 1000)
|
|
|
|
if datetime.now() < next_emission_ts:
|
|
continue
|
|
|
|
next_emission_ts += timedelta(
|
|
seconds=poisson_interval_sec(self.emission_rate_per_min)
|
|
)
|
|
|
|
if not self.redundant_real_packet_queue.empty():
|
|
addr, packet = self.redundant_real_packet_queue.get()
|
|
self.outbound_socket.put((addr, packet))
|
|
continue
|
|
|
|
if not self.real_packet_queue.empty():
|
|
addr, packet = self.real_packet_queue.get()
|
|
# Schedule redundant real packets
|
|
for _ in range(self.redundancy - 1):
|
|
self.redundant_real_packet_queue.put((addr, packet))
|
|
self.outbound_socket.put((addr, packet))
|
|
|
|
packet, route = PacketBuilder.drop_cover(
|
|
b"drop cover", self.mixnet, self.topology
|
|
).next()
|
|
self.outbound_socket.put((route[0].addr, packet))
|