reliable-transport/stopwait.py
2026-10-19 13:24:25 -06:00

101 lines
3.7 KiB
Python

"""Stop-and-wait: a complete, working reliable protocol with a window of one. Given.
Read this before you write gbn.py. It shows every piece your protocols need:
splitting the data, the retransmission timer, what the receiver ACKs, and how
the two sides agree the transfer is over (FIN, FINACK, and the receiver's
linger). Its receiver is already a correct Go-Back-N receiver.
ACKs here are cumulative: ACK n means "I have every packet before n; send me n".
"""
import socket
import time
import packet
class Sender:
def __init__(self, sock, peer, window=1, timeout=0.1, max_fin_tries=20):
self.sock, self.peer = sock, peer
self.timeout = timeout
self.max_fin_tries = max_fin_tries
self.stats = {"data_packets": 0, "transmissions": 0, "retransmissions": 0}
def send(self, data):
"""Deliver `data` reliably, then close. Returns the stats dict."""
chunks = packet.split(data)
self.stats["data_packets"] = len(chunks)
for seq, chunk in enumerate(chunks):
self._until_acked(packet.encode(packet.DATA, seq, chunk), seq + 1)
self._close(len(chunks))
return self.stats
def _until_acked(self, raw, want):
first = True
while True:
self.sock.sendto(raw, self.peer)
self.stats["transmissions"] += 1
if not first:
self.stats["retransmissions"] += 1
first = False
deadline = time.monotonic() + self.timeout
while time.monotonic() < deadline:
self.sock.settimeout(max(0.001, deadline - time.monotonic()))
try:
got = packet.decode(self.sock.recvfrom(2048)[0])
except socket.timeout:
break
if got and got[0] == packet.ACK and got[1] >= want:
return
def _close(self, count):
fin = packet.encode(packet.FIN, count)
for _ in range(self.max_fin_tries):
self.sock.sendto(fin, self.peer)
self.sock.settimeout(self.timeout)
try:
got = packet.decode(self.sock.recvfrom(2048)[0])
except socket.timeout:
continue
if got and got[0] == packet.FINACK:
return
# every data packet was ACKed, so the data arrived; only the goodbye was lost
class Receiver:
def __init__(self, sock, linger=0.5):
self.sock = sock
self.linger = linger
def receive(self):
"""Return every byte the sender sent, in order."""
expected, parts, peer = 0, [], None
self.sock.settimeout(None)
while True:
raw, peer = self.sock.recvfrom(2048)
got = packet.decode(raw)
if got is None:
continue # corrupted: say nothing
kind, seq, payload = got
if kind == packet.DATA:
if seq == expected:
parts.append(payload)
expected += 1
self.sock.sendto(packet.encode(packet.ACK, expected), peer)
elif kind == packet.FIN and seq == expected:
self._finish(peer)
return b"".join(parts)
def _finish(self, peer):
"""Answer FIN, then keep answering repeats until the line goes quiet."""
finack = packet.encode(packet.FINACK, 0)
self.sock.sendto(finack, peer)
self.sock.settimeout(self.linger)
while True:
try:
raw, peer = self.sock.recvfrom(2048)
except socket.timeout:
return
got = packet.decode(raw)
if got and got[0] == packet.FIN:
self.sock.sendto(finack, peer)