Python-使用选择器的非阻塞套接字 [英] Python - non-blocking sockets using selectors

查看:88
本文介绍了Python-使用选择器的非阻塞套接字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题简而言之: 我不知道选择器如何知道哪个套接字应该首先读取或写入.

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

会发生这种情况,但是如果创建的服务器套接字首先写入,则连接将立即关闭或抛出异常(?),因为它甚至不调用send并且客户端套接字将不会接收任何内容.那么选择器如何知道哪些套接字应该首先准备好进行写/读呢?我想了解哪些信息?

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. So how does the selector know which sockets should be ready to write/read first? Which information do i miss to understand this?

服务器:

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)

客户:

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)

推荐答案

套接字实际上并不直接向对等体写入,也不从对等体读取.相反,它们写入本地套接字特定的写缓冲区,并从套接字特定的读缓冲区中读取. OS内核关心将数据从套接字写缓冲区传递到对等方,并将从对等方接收到的数据包放入套接字接收缓冲区中.

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.

可以使用selectpollkqueue之类的功能来监视这些内核套接字缓冲区的状态以及对这些缓冲区的更改.本质上:如果套接字写缓冲区中有空间,则认为该套接字可写.如果套接字读取缓冲区中有数据,则认为该套接字可读.

The status of these in-kernel socket buffers and changes to these buffers can be monitored with functions like select, poll, kqueue. 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.

这篇关于Python-使用选择器的非阻塞套接字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆