接一条、回一句

👁️ 3 人浏览 💬 0 人评论 ❤️ 添加收藏

(每道题开头都有同一段:上面的内存版 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 在这儿会阻塞等)

服务端接一条连接,收到什么原样回去;客户端发 "abc" 再收。看客户端拿到什么、监听 socket 还在不在:

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)
cli = connect(net, 9000)
srv = lis.accept()
cli.sendall(b"abc")
data = srv.recv(100)
srv.sendall(data)
print(cli.recv(100).decode() + "/" + str(9000 in net.listeners))
提交你的答案
请登录后提交答案。
去登录
代码编辑器
Ctrl + Enter 运行
本次输入:
输出:

                        
👩‍🏫
AI
💬 题目评论

全部评论