存一个再取回来
用 roundtrip 先 PUT k v、再 GET k,第二次交回什么?
贯穿全条的内存版 TCP(判题机不联网;接口和真 socket 一样,真机上换成 socket.socket() 就是真的):net = Net();lis = listen(net, 端口) 开监听;cli = connect(net, 端口) 客户端连上(交回客户端这头,服务端那头进 accept 队列);srv = lis.accept() 领一条连接;sendall 把字节追加进对面收缓冲,recv(n) 从自己收缓冲取最多 n 字节(拿到多少算多少,recv 交回 b"" 表示对面关了)。
贯穿的应用层协议(一行一命令、按换行切):req(*parts) 用空格连、末尾加换行;decode(line) 拆成 (命令, 参数表);handle(store, line):PUT k v→OK、GET k→值或 NONE、DEL k→OK/NONE、其它→ERR。
客户端:send_req(cli,*parts) 发一条;read_reply(cli) 收一行回复去掉换行;roundtrip(net,port,store,*parts) 一次完整来回。
import collections
class Net:
def __init__(self):
self.listeners = {}
class Endpoint:
def __init__(self, name):
self.name = name
self.rbuf = b""
self.peer = None
self.peer_closed = False
self.closed = False
def send(self, data):
if self.closed or self.peer is None:
raise BrokenPipeError("连接已关")
self.peer.rbuf += bytes(data)
return len(data)
def sendall(self, data):
self.send(data)
def recv(self, bufsize):
if self.rbuf:
out, self.rbuf = self.rbuf[:bufsize], self.rbuf[bufsize:]
return out
if self.peer_closed:
return b""
raise BlockingIOError("暂时没有数据(真 socket 会在这里阻塞等)")
def close(self):
self.closed = True
if self.peer is not None:
self.peer.peer_closed = True
class Listener:
def __init__(self):
self.backlog = collections.deque()
def accept(self):
if not self.backlog:
raise BlockingIOError("暂时没有新连接")
return self.backlog.popleft()
def listen(net, port):
lis = Listener()
net.listeners[port] = lis
return lis
def connect(net, port):
if port not in net.listeners:
raise ConnectionRefusedError(111, "Connection refused")
cli = Endpoint("client")
srv = Endpoint("server")
cli.peer = srv
srv.peer = cli
net.listeners[port].backlog.append(srv)
return cli
def req(*parts):
return (" ".join(parts) + "\n").encode()
def decode(line):
bits = line.decode().split(" ")
return bits[0], bits[1:]
def handle(store, line):
# PUT k v -> OK;GET k -> 值 or NONE;DEL k -> OK/NONE;别的 -> ERR
cmd, args = decode(line)
if cmd == "PUT" and len(args) == 2:
store[args[0]] = args[1]
return b"OK\n"
if cmd == "GET" and len(args) == 1:
return (store.get(args[0], "NONE") + "\n").encode()
if cmd == "DEL" and len(args) == 1:
return (b"OK\n" if store.pop(args[0], None) is not None else b"NONE\n")
return b"ERR\n"
def send_req(cli, *parts):
# 客户端发一条请求:用空格连、末尾加换行
cli.sendall((" ".join(parts) + "\n").encode())
def read_reply(cli):
# 收一行回复、去掉末尾换行、解码成字符串
return cli.recv(4096).split(b"\n", 1)[0].decode()
def roundtrip(net, port, store, *parts):
# 一次完整来回:客户端发、服务端 handle、客户端读回复
cli = connect(net, port)
srv = net.listeners[port].backlog[-1]
send_req(cli, *parts)
srv.sendall(handle(store, srv.recv(4096).split(b"\n", 1)[0]))
return read_reply(cli)
net = Net()
lis = listen(net, 9000)
store = {}
roundtrip(net, 9000, store, "PUT", "k", "v")
print(roundtrip(net, 9000, store, "GET", "k"))
全部评论