攒到一行
连接里陆续来了 one\ntwo,recv_line 交回什么?(bytes)
贯穿全条的内存版 TCP(判题机不联网;接口和真 socket 一样,真机上换成 socket.socket() 就是真的):net = Net();lis = listen(net, 端口) 开监听;cli = connect(net, 端口) 客户端连上(交回客户端这头,服务端那头进 accept 队列);srv = lis.accept() 领一条连接;sendall 把字节追加进对面收缓冲,recv(n) 从自己收缓冲取最多 n 字节(拿到多少算多少,recv 交回 b"" 表示对面关了)。
断连与错误:recv_line(sock) 攒到换行为止交回这一行;closed(sock) 判对面关没;safe_send(sock,data) 对面已关就不发、交回 False。
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 recv_line(sock):
# 攒字节到一个换行为止,交回这一行(不含换行);对面关了又没换行,交回已收到的
buf = b""
while True:
try:
chunk = sock.recv(4096)
except BlockingIOError:
return buf
if not chunk:
return buf
buf += chunk
if b"\n" in buf:
return buf.split(b"\n", 1)[0]
def closed(sock):
# 对面关了吗(recv 交回空就是关了)
return sock.peer_closed and not sock.rbuf
def safe_send(sock, data):
# 对面已关就别发(返回 False),否则发出去返回 True
if sock.closed or sock.peer_closed:
return False
sock.sendall(data)
return True
net = Net()
lis = listen(net, 9000)
cli = connect(net, 9000)
srv = lis.accept()
cli.sendall(b"one\ntwo")
print(recv_line(srv))
全部评论