Обработка нескольких запросов с помощью выбора

В настоящее время я работаю над проектом сервера / клиента чата. Я борюсь с обработкой нескольких запросов с помощью 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()

Существуют проблемы как на стороне сервера, так и на стороне клиента, которые не позволяют вашему приложению работать в режиме реального времени. Вот несколько, которые я заметил до сих пор:

  1. Клиент читает данные из соединения с сервером только после записи некоторых данных в сокет. Попробуйте поместить логику для чтения из сокета в отдельный поток.

  2. На вашем сервере вы перебираете rlist вернулся select() перед отправкой ожидающих сообщений клиенту; клиентские fds будут присутствовать в rlist, только если клиент отправил сообщение. Отправка сообщений должна производиться на основе записываемых fds путем итерации по wlist. Но у этого есть другие проблемы...

  3. Ты всегда select() на возможность записи для всех клиентских fds, даже если нет ожидающих сообщений для записи этому клиенту. Конечным результатом является то, что ваш select() call почти всегда сразу возвращается и теряет процессор (что побуждает цель select)

  4. Весь ваш сокет ввода / вывода выполняется в режиме "блокировки", поэтому ваш send() может блокировать, если вы отправляете больше данных, которые может обрабатывать приемный буфер ядра на удаленном конце (обычно это около 10 МБ).

Вам будет гораздо лучше использовать асинхронную среду (например, витую) для реализации приложений такого типа. Управление всеми буферами может быть утомительным и сложным, и эта работа уже проделана.

Другие вопросы по тегам