Manejar múltiples solicitudes con seleccionar

Actualmente estoy trabajando en un servidor de chat / proyecto de cliente. Estoy luchando con el manejo de múltiples solicitudes con select, mi script del servidor usa el módulo select pero el script del cliente no. El resultado es que, cuando un usuario ingresa un mensaje, los otros clientes tienen que escribir su propio mensaje para leer la conversación. He buscado muchos ejemplos en la web, pero solo pude encontrar fragmentos de código con sys.stdin que no es lo que quiero.

Me encantaría recibir cualquier instrucción / explicación.

Código del servidor:

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" + ' ') # send_waiting_messages(wlist) # send message to specfic client server_socket.close() 

Codigo del cliente:

 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() 

mi script del servidor utiliza el módulo de selección, pero el script del cliente no lo hace.

Una solución es utilizar select también en el cliente. En Windows desafortunadamente, select no maneja sys.stdin , pero podemos usar el argumento de tiempo de espera para sondear el teclado.

 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() 

Hay problemas tanto en el servidor como en el lado del cliente que impiden que su aplicación sea realmente en tiempo real. Aquí hay varios que he notado hasta ahora:

  1. El cliente está leyendo datos de la conexión del servidor solo después de escribir algunos datos en el socket. Considere poner la lógica para leer desde el socket en un hilo separado.

  2. En su servidor, rlist el rlist devuelto por select() antes de enviar mensajes pendientes al cliente; los fds del cliente solo estarán presentes en rlist si el cliente ha enviado un mensaje. El envío de mensajes debe hacerse en base a archivos grabables, iterando en wlist. Pero esto tiene otros problemas …

  3. Siempre select() capacidad de escritura para todos los fds de clientes, incluso si no hay mensajes pendientes para escribir en ese cliente. El resultado final es que su llamada select() casi siempre regresará de inmediato, y desperdiciará la CPU (que tipo de derrotas el propósito de seleccionar)

  4. Todo su IO de socket se realiza en modo “locking”, por lo que su send() puede bloquear si envía más datos que el búfer de recepción del kernel en el extremo remoto puede manejar (por lo general, alrededor de 10 MB).

Estará mucho mejor usando un marco asíncrono (como trenzado) para implementar este tipo de aplicación. La gestión de todos los buffers puede ser tediosa y desafiante, y es un trabajo que ya se ha hecho antes.