简体   繁体   English

Python - 使用选择器的非阻塞套接字

[英]Python - non-blocking sockets using selectors

My Problem in short: I dont know how the selector knows which socket should read or write first.我的问题简而言之:我不知道选择器如何知道应该先读取或写入哪个套接字。

It is a Server that can handle multi connections and its flow should be:它是一个可以处理多连接的服务器,它的流程应该是:

  1. Server creates listening socket服务器创建监听套接字
  2. Client creates 2 sockets and connects them to the server客户端创建 2 个套接字并将它们连接到服务器
  3. Client 2 sockets send the messages客户端 2 套接字发送消息
  4. Server 2 sockets echo those messages, client and server closing connection服务器 2 套接字回显这些消息,客户端和服务器关闭连接

which is what happens, but if the created server sockets would write first, the connection would be closed immediately or throw an exception(?), since it doesn't even call send and the client socket would recv nothing.这就是发生的情况,但是如果创建的服务器套接字首先写入,则连接将立即关闭或抛出异常(?),因为它甚至不调用 send 并且客户端套接字不会接收任何内容。 So how does the selector know which sockets should be ready to write/read first?那么选择器如何知道哪些套接字应该首先准备好写入/读取? Which information do i miss to understand this?我错过了哪些信息才能理解这一点?

Server:服务器:

import socket
import selectors
import types

host = "127.0.0.1"
port = 63210

def accept_wrapper(sock):
    conn, addr = sock.accept()
    print('accepted connection from', addr)
    conn.setblocking(False)
    data = types.SimpleNamespace(addr=addr, inb=b'', outb=b'')
    events = selectors.EVENT_READ | selectors.EVENT_WRITE
    sel.register(conn, events, data=data)

def service_connection(key, mask):
    sock = key.fileobj
    data = key.data
    if mask & selectors.EVENT_READ:
        recv_data = sock.recv(1024)
        if recv_data:
            data.outb += recv_data
        else:
            print('closing connection to', data.addr)
            sel.unregister(sock)
            sock.close()
    if mask & selectors.EVENT_WRITE:
        if data.outb:
            print('echoing', repr(data.outb), 'to', data.addr)
            sent = sock.send(data.outb)
            data.outb = data.outb[sent:]

sel = selectors.DefaultSelector()

lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
lsock.bind((host, port))
lsock.listen()
print('listening on', (host, port))
lsock.setblocking(False)
sel.register(lsock, selectors.EVENT_READ, data=None)

while True:
    events = sel.select(timeout=None)
    for key, mask in events:
        if key.data is None:
            accept_wrapper(key.fileobj)
        else:
            service_connection(key, mask)

Client:客户:

import socket
import selectors
import types

host = "127.0.0.1"
port = 63210
num_conns = 2
messages = [b'Message 1 from client.', b'Message 2 from client.']

def start_connections(host, port, num_conns):
    server_addr = (host, port)
    for i in range(0, num_conns):
        connid = i + 1
        print('starting connection', connid, 'to', server_addr)
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setblocking(False)
        sock.connect_ex(server_addr)
        events = selectors.EVENT_READ | selectors.EVENT_WRITE
        data = types.SimpleNamespace(connid=connid,
                                     msg_total=sum(len(m) for m in messages),
                                     recv_total=0,
                                     messages=list(messages),
                                     outb=b'')
        sel.register(sock, events, data=data)

def service_connection(key, mask):
    sock = key.fileobj
    data = key.data
    if mask & selectors.EVENT_READ:
        recv_data = sock.recv(1024)
        if recv_data:
            print('received', repr(recv_data), 'from connection', data.connid)
            data.recv_total += len(recv_data)
        if not recv_data or data.recv_total == data.msg_total:
            print('closing connection', data.connid)
            sel.unregister(sock)
            sock.close()
    if mask & selectors.EVENT_WRITE:
        if not data.outb and data.messages:
            data.outb = data.messages.pop(0)
        if data.outb:
            print('sending', repr(data.outb), 'to connection', data.connid)
            sent = sock.send(data.outb)
            data.outb = data.outb[sent:]

sel = selectors.DefaultSelector()
start_connections(host, port, num_conns)

while True:
    events = sel.select(timeout=None)
    for key, mask in events:
        service_connection(key, mask)

Sockets don't actually write directly to the peer and they don't read from the peer.套接字实际上并不直接写入对等方,也不从对等方读取。 Instead they write into a local socket specific write buffer and read from a socket specific read buffer.相反,它们写入本地套接字特定的写入缓冲区并从套接字特定的读取缓冲区中读取。 The OS kernel cares about the delivery of the data from the socket write buffer to the peer and puts received packets from the peer into the sockets receive buffer.操作系统内核负责将数据从套接字写入缓冲区传送到对等方,并将从对等方接收到的数据包放入套接字接收缓冲区。

The status of these in-kernel socket buffers and changes to these buffers can be monitored with functions like select , poll , kqueue .这些内核套接字缓冲区的状态和对这些缓冲区的更改可以使用selectpollkqueue等函数进行监视。 In essence: a socket is considered writable if there is room in the sockets write buffer.本质上:如果套接字写入缓冲区中有空间,则认为套接字可写。 A socket is considered readable if there are data in the sockets read buffer.如果套接字读取缓冲区中有数据,则认为套接字是可读的。

I have looked at the same code that you have, and have raised a pull request for precisely the concern that you have: https://github.com/realpython/materials/pull/112我查看了您拥有的相同代码,并针对您所关注的问题提出了拉取请求: https : //github.com/realpython/materials/pull/112

Essentially, the answer is that the sockets and read+write events are cycled by the system in a loop.本质上,答案是套接字和读+写事件由系统在一个循环中循环。 Eventually every socket becomes available to read and write in this loop and you just have to wait for the turn to occur.最终每个套接字都可以在这个循环中读写,你只需要等待轮到发生。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM