使用select的非阻塞I / O

时间:2019-05-10 14:18:43

标签: python python-3.x sockets

我有一个示例客户端-服务器程序,该程序对不使用进程或线程的多个套接字执行非阻塞I / O。它使用select。不幸的是,服务器仅显示许多空白行,仅此而已。错误在哪里?
在MacOS上运行。

谢谢。

服务器:

import socket
import select

sock = socket.socket()
sock.bind(('', 10001))
sock.listen()

conn1, _ = sock.accept()
conn2, _ = sock.accept()

conn1.setblocking(0)
conn2.setblocking(0)

epoll = select.poll()
epoll.register(conn1.fileno(), select.POLLIN | select.POLLOUT)
epoll.register(conn2.fileno(), select.POLLIN | select.POLLOUT)

conn_map = {
    conn1.fileno(): conn1,
    conn2.fileno(): conn2,
}

while True:
    events = epoll.poll(1)
    for fileno, event in events:
        if event & select.POLLIN:
            data = conn_map[fileno].recv(1024)
            print(data.decode('utf8'))
        elif event & select.POLLOUT:
            conn_map[fileno].send('ping'.encode('utf8'))

客户

import socket
from multiprocessing import Pool

def create_socket_and_send_data(number):
    with socket.create_connection(('127.0.0.1', 10001)) as sock:
        try:
            sock.sendall(f'client {number}\n'.encode('utf8'))
        except socket.error as ex:
            print('data sending error', ex)

    print(f'data for {number} has been sent')

if __name__ == '__main__':
    with Pool(processes=2) as pool:
        pool.map(create_socket_and_send_data, range(2))

2 个答案:

答案 0 :(得分:3)

  

不幸的是,服务器仅显示很多空白行,仅此而已。

实际上这不是事实。 服务器在从客户端获得的行的开头打印。发送完这些行后,客户端将关闭连接,这意味着select.POLLIN在套接字上再次被触发,并且recv返回空数据。

此空数据是对等方已关闭连接的标志。一旦收到此标志,服务器应关闭与客户端的连接,并从fileno中删除select。相反,您的服务器使用换行符打印空字符串,并继续期待新的POLLIN事件。这些将一次又一次地出现,并且始终是一个空缓冲区,从而导致您看到的所有空行。

答案 1 :(得分:2)

矛盾的是,

select用于输入比用于输出更容易使用。对于输入,每次有新数据到达套接字时,您都会收到一个事件,因此,您始终会请求所有套接字,并为每个新事件处理一些事情。

对于输出,select仅表示准备好接受新数据的套接字。除非您已填满缓冲区,否则几乎总是如此。因此,只应在有写东西的地方轮询输出套接字。

因此,您应该仅使用select.POLLIN注册套接字。对于写部分,如果可以希望对等端始终能够接收,则应该直接写一个套接字而不进行轮询,或者设置一个队列,每个套接字的挂起输出modify的轮询状态当队列中有内容时,使用select.POLLIN | select.POLLOUT进行套接字;当队列再次为空时,使用select.POLLIN进行修改。