неблокирующий ввод / вывод с помощью select - PullRequest
0 голосов
/ 10 мая 2019

У меня есть пример клиент-серверной программы, которая выполняет неблокирующий ввод / вывод для нескольких сокетов, не используя процессы или потоки.Использует 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 ]

3 голосов
/ 10 мая 2019

К сожалению, сервер просто показывает много пустых строк, и все.

На самом деле это не так. Сервер в начале печатает строки, полученные от клиентов. После отправки этих строк клиент закрывает соединение, что означает, что select.POLLIN снова запускается в сокете, а recv возвращает пустые данные.

Эти пустые данные являются признаком того, что узел закрыл соединение. Как только он получил этот знак, сервер должен закрыть соединение с клиентом и удалить fileno из select. Вместо этого ваш сервер печатает пустую строку с новой строкой и продолжает ожидать новых событий POLLIN. Они будут приходить снова и снова и всегда будут пустым буфером, что приведет ко всем пустым строкам, которые вы видите.

2 голосов
/ 10 мая 2019

select парадоксально проще использовать для ввода, чем для вывода. Для ввода вы получаете событие каждый раз, когда в сокет поступают новые данные, поэтому вы всегда запрашиваете все сокеты и имеете что-то для обработки для каждого нового события.

Для вывода select просто скажет, что сокет, если готов принять новые данные. Что почти всегда верно, кроме случаев, когда вы только что заполнили буфер. Поэтому вы должны запрашивать выходной сокет только тогда, когда вам есть что написать.

Таким образом, вы должны регистрировать свои сокеты только с select.POLLIN. Для части записи вы должны либо напрямую записать в сокет без опроса, если вы можете надеяться, что узел всегда будет способен получать, либо настроить очередь с ожидающим выводом на сокет, modify состояние опроса сокета с select.POLLIN | select.POLLOUT когда в его очереди что-то есть и изменить его обратно с помощью select.POLLIN, когда очередь снова пуста.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...