顺序服务:谁等谁
(每道题开头都有同一段:上面的内存版 TCP。)
贯穿全条的内存版 TCP(判题机不联网;接口和真 socket 一样,真机上把它换成 socket.socket() 就是真的):
net = Net()
lis = listen(net, 9000) 服务端开监听
cli = connect(net, 9000) 客户端连上——交回客户端这一头;服务端那一头进 accept 队列
srv = lis.accept() 服务端领一条新连接(一个新端点,lis 继续监听)
cli.sendall(b"...") / srv.recv(n) send 把字节追加进对面的收缓冲;recv 从自己的收缓冲拿最多 n 字节——拿到多少算多少,不保证是「一条消息」
x.close() 关了之后,对面 recv 交回 b"";收缓冲空又没关时 recv 抛 BlockingIOError(真 socket 在这儿会阻塞等)单线程按队列顺序服务:三个客户端 A/B/C 先后连上,服务端一条条处理。打印处理顺序,以及处理 A 时队列里还排着几个:
import collections
class Net:
"""内存版的网络:端口 -> 监听者。判题机不联网,接口和真 socket 一样,真机上把这些换成 socket.socket() 就是真的。"""
def __init__(self):
self.listeners = {}
class Endpoint:
"""一条连接的一头:自己的收缓冲 rbuf,写就写进对面的 rbuf。"""
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 # 字节流:给你缓冲里现有的,最多 bufsize,不保证是「一条消息」
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("暂时没有新连接(真 socket 会在 accept 阻塞)")
return self.backlog.popleft() # 交回服务端那一头的 Endpoint
def listen(net, port):
lis = Listener()
net.listeners[port] = lis
return lis
def connect(net, port):
"""建一对相连的端点:客户端这头交回给调用方,服务端那头塞进监听队列等 accept。"""
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
net = Net()
lis = listen(net, 9000)
for name in [b"A", b"B", b"C"]:
connect(net, 9000).sendall(name)
order = []
queued_when_first = len(lis.backlog) - 1
while lis.backlog:
conn = lis.accept()
order.append(conn.recv(100).decode())
conn.close()
print("".join(order) + "/" + str(queued_when_first))
全部评论