Обработка нескольких запросов с помощью выбора
В настоящее время я работаю над проектом сервера / клиента чата. Я борюсь с обработкой нескольких запросов с помощью select, мой серверный скрипт использует модуль select, а клиентский скрипт - нет. В результате, когда пользователь вводит сообщение, другие клиенты должны написать свое собственное сообщение, чтобы прочитать диалог. Я много искал в Интернете примеры, но смог найти только фрагменты кода с sys.stdin, а это не то, что мне нужно.
Я был бы рад получить любую инструкцию / объяснение.
Код сервера:
import socket
import select
server_socket = socket.socket()
server_socket.bind(("0.0.0.0", 2855))
server_socket.listen(1)
open_client_sockets = [] # current clients handler
messages_to_send = [] # future message send handler
def send_waiting_messages(wlist):
for message in messages_to_send:
(client_socket, data) = message
if client_socket in wlist: # if current socket in iteration has reading abilities
client_socket.send(data)
messages_to_send.remove(message) # remove from future send handler
def broadcast_message(sock, message):
for socket in open_client_sockets:
if socket != server_socket and socket != sock:
socket.send(message)
while True:
rlist, wlist, xlist = select.select([server_socket] + open_client_sockets, open_client_sockets, []) # apending reading n writing socket to list
for current_socket in rlist: # sockets that can be read
if current_socket is server_socket: # if there is a new client
(new_socket, address) = server_socket.accept()
open_client_sockets.append(new_socket) # clients list
else:
data = current_socket.recv(1024)
if len(data) == 0:
open_client_sockets.remove(current_socket) # remove user if he quit.
print "Connection with client closed."
send_waiting_messages(wlist) # send message to specfic client
else:
broadcast_message(current_socket, "\r" + '<' + data + '> ')
# send_waiting_messages(wlist) # send message to specfic client
server_socket.close()
Код клиента:
import socket
import msvcrt
client_socket = socket.socket()
client_socket.connect(("127.0.0.1", 2855))
data = ""
def read_message():
msg = ""
while True:
if msvcrt.kbhit():
key = msvcrt.getch()
if key == '\r': # Enter key
break
else:
msg = msg + "" + key
return msg
while data != "quit":
data = read_message()
client_socket.send(data)
data = client_socket.recv(1024)
print data
client_socket.close()
2 ответа
мой серверный скрипт использует модуль select, а клиентский - нет.
Решение заключается в использовании select
также в клиенте. На винде к сожалению select
не обрабатывает sys.stdin
, но мы можем использовать аргумент timeout для опроса клавиатуры.
import socket
import select
import msvcrt
client_socket = socket.socket()
client_socket.connect(("localhost", 2855))
msg = ""
while True:
ready = select.select([client_socket], [], [], .1)
if client_socket in ready[0]:
data = client_socket.recv(1024)
print data, ' '*(len(msg)-len(data))
print msg,
if msvcrt.kbhit():
key = msvcrt.getche()
if key == '\r': # Enter key
if msg == "quit":
break
client_socket.send(msg)
msg = ""
print
else:
msg = msg + "" + key
client_socket.close()
Существуют проблемы как на стороне сервера, так и на стороне клиента, которые не позволяют вашему приложению работать в режиме реального времени. Вот несколько, которые я заметил до сих пор:
Клиент читает данные из соединения с сервером только после записи некоторых данных в сокет. Попробуйте поместить логику для чтения из сокета в отдельный поток.
На вашем сервере вы перебираете
rlist
вернулсяselect()
перед отправкой ожидающих сообщений клиенту; клиентские fds будут присутствовать в rlist, только если клиент отправил сообщение. Отправка сообщений должна производиться на основе записываемых fds путем итерации по wlist. Но у этого есть другие проблемы...Ты всегда
select()
на возможность записи для всех клиентских fds, даже если нет ожидающих сообщений для записи этому клиенту. Конечным результатом является то, что вашselect()
call почти всегда сразу возвращается и теряет процессор (что побуждает цель select)Весь ваш сокет ввода / вывода выполняется в режиме "блокировки", поэтому ваш
send()
может блокировать, если вы отправляете больше данных, которые может обрабатывать приемный буфер ядра на удаленном конце (обычно это около 10 МБ).
Вам будет гораздо лучше использовать асинхронную среду (например, витую) для реализации приложений такого типа. Управление всеми буферами может быть утомительным и сложным, и эта работа уже проделана.