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

87 lines
2.8 KiB
Python

"""Send or receive one file over real UDP with any of the three protocols. Given.
python3 transfer.py recv --proto gbn --port 5000 --out got.bin
python3 transfer.py send --proto gbn --window 8 --timeout 0.1 10.0.0.2 5000 file.bin
The receiver prints nothing until the transfer ends; the sender prints one
line of JSON with its stats and the elapsed time. experiments.py runs both
inside Mininet for you, so you rarely need this by hand.
Reference: https://docs.python.org/3/library/socket.html
"""
import argparse
import json
import socket
import time
import gbn
import sr
import stopwait
PROTOCOLS = {"sw": stopwait, "gbn": gbn, "sr": sr}
class UdpSocket:
"""A UDP socket whose timeout applies to receiving only.
The protocols set short timeouts while they wait for ACKs. On a plain
socket that timeout also covers sendto(), which then fails when the
outgoing queue is briefly full. Here sendto() waits instead, the same way
channel.py's simulated link behaves.
"""
def __init__(self, sock):
self._sock = sock
self._timeout = None
def settimeout(self, seconds):
self._timeout = seconds
def sendto(self, data, addr):
self._sock.settimeout(None)
return self._sock.sendto(data, addr)
def recvfrom(self, bufsize):
self._sock.settimeout(self._timeout)
return self._sock.recvfrom(bufsize)
def main():
ap = argparse.ArgumentParser()
sub = ap.add_subparsers(dest="role", required=True)
r = sub.add_parser("recv")
r.add_argument("--proto", choices=PROTOCOLS, required=True)
r.add_argument("--port", type=int, default=5000)
r.add_argument("--window", type=int, default=8)
r.add_argument("--out", required=True)
s = sub.add_parser("send")
s.add_argument("--proto", choices=PROTOCOLS, required=True)
s.add_argument("--window", type=int, default=8)
s.add_argument("--timeout", type=float, default=0.1)
s.add_argument("host")
s.add_argument("port", type=int)
s.add_argument("file")
args = ap.parse_args()
raw = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock = UdpSocket(raw)
mod = PROTOCOLS[args.proto]
if args.role == "recv":
raw.bind(("0.0.0.0", args.port))
receiver_cls = getattr(mod, "Receiver", stopwait.Receiver)
kw = {"window": args.window} if receiver_cls is not stopwait.Receiver else {}
data = receiver_cls(sock, **kw).receive()
with open(args.out, "wb") as f:
f.write(data)
else:
with open(args.file, "rb") as f:
data = f.read()
start = time.monotonic()
stats = mod.Sender(sock, (args.host, args.port), window=args.window, timeout=args.timeout).send(data)
stats["seconds"] = round(time.monotonic() - start, 3)
print(json.dumps(stats))
if __name__ == "__main__":
main()