#!/usr/bin/env python3 """Real loopback TCP framing and UDP datagrams, Python standard library only.""" import json import socket import struct from concurrent.futures import ThreadPoolExecutor def read_exact(sock,count,allow_clean_eof=False): chunks=[];remaining=count while remaining: part=sock.recv(min(remaining,2)) # Force small API reads; not a claim about TCP packet size. if not part: if allow_clean_eof and remaining==count:return None raise EOFError(f'EOF with {remaining} bytes missing') chunks.append(part);remaining-=len(part) return b''.join(chunks) def receive_message(sock): header=read_exact(sock,4,allow_clean_eof=True) if header is None:return None size=struct.unpack('!I',header)[0] if size>4096:raise ValueError('message exceeds lab limit') return read_exact(sock,size) def demo(): messages=[b'cat','網路'.encode('utf-8'),b''] with socket.socket() as server,ThreadPoolExecutor(max_workers=1) as pool: server.bind(('127.0.0.1',0));server.listen();server.settimeout(5) def serve(): with server.accept()[0] as conn: conn.settimeout(5) result=[] while True: message=receive_message(conn) if message is None:return result result.append(message.decode('utf-8')) future=pool.submit(serve) with socket.create_connection(server.getsockname(),timeout=5) as client: wire=b''.join(struct.pack('!I',len(m))+m for m in messages) client.sendall(wire);client.shutdown(socket.SHUT_WR) tcp=future.result(timeout=6) with socket.socket(type=socket.SOCK_DGRAM) as receiver,socket.socket(type=socket.SOCK_DGRAM) as sender: receiver.bind(('127.0.0.1',0));receiver.settimeout(5) for message in messages[:2]:sender.sendto(message,receiver.getsockname()) udp=[receiver.recvfrom(4096)[0].decode('utf-8') for _ in range(2)] return {'kind':'real loopback sockets','tcp_messages':tcp,'udp_datagrams':udp, 'note':'recv chunks are limited by application; TCP segment sizes are not measured; UDP loopback demonstration assumes no loss/reordering.'} if __name__=='__main__':print(json.dumps(demo(),ensure_ascii=False,indent=2))