我有一个示例客户端-服务器程序,该程序对不使用进程或线程的多个套接字执行非阻塞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))
答案 0 :(得分:3)
不幸的是,服务器仅显示很多空白行,仅此而已。
实际上这不是事实。
服务器在从客户端获得的行的开头打印。发送完这些行后,客户端将关闭连接,这意味着select.POLLIN
在套接字上再次被触发,并且recv
返回空数据。
此空数据是对等方已关闭连接的标志。一旦收到此标志,服务器应关闭与客户端的连接,并从fileno
中删除select
。相反,您的服务器使用换行符打印空字符串,并继续期待新的POLLIN
事件。这些将一次又一次地出现,并且始终是一个空缓冲区,从而导致您看到的所有空行。
答案 1 :(得分:2)
select
用于输入比用于输出更容易使用。对于输入,每次有新数据到达套接字时,您都会收到一个事件,因此,您始终会请求所有套接字,并为每个新事件处理一些事情。
对于输出,select
仅表示准备好接受新数据的套接字。除非您已填满缓冲区,否则几乎总是如此。因此,只应在有写东西的地方轮询输出套接字。
因此,您应该仅使用select.POLLIN
注册套接字。对于写部分,如果可以希望对等端始终能够接收,则应该直接写一个套接字而不进行轮询,或者设置一个队列,每个套接字的挂起输出modify
的轮询状态当队列中有内容时,使用select.POLLIN | select.POLLOUT
进行套接字;当队列再次为空时,使用select.POLLIN
进行修改。