You call recv twice.
First:
msg=s.recv(100)
Then, if that's not "quit", you read and broadcast another message:
msg = s.recv(1024)
broadcast(msg,s)
So the original message is lost.
Because you're using telnet as the client, you get one character at a time, so you see every other character. If you used, say, nc instead, you'd get different results—but still the same basic problem of every other read being thrown away.
There are a few other problems here:
- You're expecting clients to send "quit" before quitting—you should be handling EOF or error from recv and/or passing sockets in the x as well as the r.
- You're assuming that "quit" will always appear in a single message, and an entire message all to itself. This is not a reasonable assumption with TCP. You may get four 1-byte reads of "q", "u", "i", and "t", or you may get a big read of "OK, bye everyone\nquit\n", neither of which will match.
- The "Client left" and "Client joined" messages are tuples, not strings, and they're formed differently, so you're going to see ('Client joined', "('127.0.0.1', 56564)") ('Client left', ('127.0.0.1', 56564)).
- You're relying on the clients to send newlines between their messages. First, as mentioned above, even if they did, there's no guarantee that you'll get complete/discrete messages. Second, your "system" messages don't have newlines.
Here's a modified version of your sample that fixes most of the problems, except for requiring "quit" to be in a single message alone and relying on the clients to send newlines:
#!/usr/bin/python
import socket
import select
import sys
port = 11222
serverSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1024)
serverSocket.bind(('',port))
serverSocket.listen(5)
sockets=[serverSocket]
print 'Server is started on port' , port,'\n'
def acceptConn():
newsock, addr = serverSocket.accept()
sockets.append(newsock)
newsock.send('You are now connected to the chat server\n')
msg = 'Client joined: %s:%d\n' % addr
broadcast(msg, newsock)
def broadcast(msg, sourceSocket):
for s in sockets:
if (s != serverSocket and s != sourceSocket):
s.send(msg)
sys.stdout.write(msg)
sys.stdout.flush()
while True:
(sread, swrite, sexec)=select.select(sockets,[],[])
for s in sread:
if s == serverSocket:
acceptConn()
else:
msg=s.recv(100)
if not msg or msg.rstrip() == "quit":
host,port=s.getpeername()
msg = 'Client left: %s:%d\n' % (host,port)
broadcast(msg,s)
s.close()
sockets.remove(s)
del s
else:
host,port=s.getpeername()
broadcast(msg,s)
continue
To fix the 'quit' problem, you're going to have to keep a buffer for each client, and do something like this:
buffers[s] += msg
if '\nquit\n' in buffers[s]:
# do quit stuff
lines = buffers[s].split('\n')[-1]
buffers[s] = ('\n' if len(lines) > 1 else '') + lines[-1]
But you've still got the newline problem. Imagine that user1 logs in and types "abc\n" while user2 logs in and types "def\n"; you may get something like "abClient joined: 127.0.0.1:56881\ndec\nf\n".
If you want a line-based protocol, you have to rewrite your code to do the echoing on a line-by-line instead of read-by-read basis.