Reputation: 47
currently I am working on a chat server/client project. I am struggling with handling multiple requests with select, my server script uses the select module but the client script doesn't. The result is that when ever a user enters message the other clients have to write their own message to read through the conversation. I have searched a lot in the web for examples, but could only find code snippets with sys.stdin which isn't what I want.
I would glad to receive any instruction/explanation.
Server code:
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()
Client code:
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()
Upvotes: 3
Views: 3499
Reputation: 19385
my server script uses the select module but the client script doesn't.
A solution is to use select
also in the client. On Windows unfortunately select
does not handle sys.stdin
, but we can use the timeout argument to poll the keyboard.
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()
Upvotes: 2
Reputation: 799
There are issues on both the server and client side that prevent your application from being truly realtime. Here are several that I've noticed so far:
The client is reading data from the server connection only after writing some data to the socket. Consider putting the logic to read from the socket into a separate thread.
In your server, you iterate across the rlist
returned by select()
before sending pending messages to the client; the client fds will only be present in rlist if the client has sent a message. Sending messages should be done based on writeable fds, by iterating across wlist. But this has other problems...
You always select()
on writeability for all client fds, even if there's no pending messages to write to that client. The end result is that your select()
call will nearly always return immediately, and will waste CPU (which sort of defeats the purpose of select)
All your socket IO is done in "blocking" mode, so your send()
may block if you send more data that the kernel's receive buffer on the remote end can handle (typically this is around 10MB).
You'll be much better off using an asynchronous framework (such as twisted) for implementing this type of application. Managing all the buffers can be tedious and challenging, and it's work that's already been done before.
Upvotes: 1