Python套接字错误 - recv()函数

时间:2012-07-09 18:45:28

标签: python sockets python-2.7 recv python-sockets

我一直在尝试用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。 有什么帮助吗?! :)

1 个答案:

答案 0 :(得分:4)

你打电话给recv两次。

首先:

msg=s.recv(100)

然后,如果那不是“退出”,你会阅读并播放另一条消息:

msg = s.recv(1024)
broadcast(msg,s)

因此原始邮件丢失了。

因为您使用telnet作为客户端,所以您一次只能获得一个字符,因此您可以看到所有其他字符。如果你使用nc代替,那么你会得到不同的结果 - 但仍然会抛弃所有其他读取的基本问题。

这里还有一些其他问题:

  • 您希望客户在退出之前发送“退出” - 您应该处理EOF或来自recv的错误和/或在x和r中传递套接字。
  • 您假设“退出”将始终出现在单个消息中,并且整个消息都将出现在自身中。这不是TCP的合理假设。您可能会得到四个1字节读取“q”,“u”,“i”和“t”,或者您可能会读到“OK,bye everyone \ nquit \ n”,两者都不匹配
  • “客户端离开”和“客户端加入”消息是元组,而不是字符串,它们的形式不同,因此您将看到('Client joined',“('127.0.0.1',56564) “)('客户离开',('127.0.0.1',56564))。
  • 您依靠客户在其邮件之间发送换行符。首先,如上所述,即使他们这样做,也无法保证您将获得完整/离散的消息。其次,您的“系统”消息没有换行符。

以下是您的示例的修改版本,修复了大多数问题,除了要求“退出”仅在单个邮件中并依赖客户端发送换行符:

#!/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”的内容。

如果你想要一个基于行的协议,你必须重写你的代码,以逐行而不是逐读的方式进行回显。