我一直在尝试用Python编写一个简单的聊天服务器,我的代码如下:
import socket
import select
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',addr.__str__(),
broadcast(msg, newsock)
def broadcast(msg, sourceSocket):
for s in sockets:
if (s != serverSocket and s != sourceSocket):
s.send(msg)
print msg,
while True:
(sread, swrite, sexec)=select.select(sockets,[],[])
for s in sread:
if s == serverSocket:
acceptConn()
else:
msg=s.recv(100)
if msg.rstrip() == "quit":
host,port=socket.getpeername()
msg = 'Client left' , (host,port)
broadcast(msg,s)
s.close()
sockets.remove(s)
del s
else:
host,port=s.getpeername()
msg = s.recv(1024)
broadcast(msg,s)
continue
运行服务器并通过telnet连接后,服务器会读取单个字符并跳过下一个字符。例如,如果我在telnet中键入Hello,则服务器读取H l o。 有什么帮助吗?! :)
答案 0 :(得分:4)
你打电话给recv两次。
首先:
msg=s.recv(100)
然后,如果那不是“退出”,你会阅读并播放另一条消息:
msg = s.recv(1024)
broadcast(msg,s)
因此原始邮件丢失了。
因为您使用telnet作为客户端,所以您一次只能获得一个字符,因此您可以看到所有其他字符。如果你使用nc代替,那么你会得到不同的结果 - 但仍然会抛弃所有其他读取的基本问题。
这里还有一些其他问题:
以下是您的示例的修改版本,修复了大多数问题,除了要求“退出”仅在单个邮件中并依赖客户端发送换行符:
#!/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
要解决“退出”问题,您必须为每个客户端保留一个缓冲区,并执行以下操作:
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]
但是你仍然遇到换行问题。想象一下,当user2登录并输入“def \ n”时,user1登录并键入“abc \ n”;你可能会得到类似“abClient加入:127.0.0.1:56881 \ nnccnnn”的内容。
如果你想要一个基于行的协议,你必须重写你的代码,以逐行而不是逐读的方式进行回显。