客户端使用python套接字接收两个单独的消息

时间:2015-02-18 12:03:46

标签: python sockets

我按如下方式定义服务器类(编辑):

class server:
    def __init__( self, ip = "", port = 0 ):
        self.SetAddress( ip, port )
        self.__players = []

    def __SetSocket( self, blocking = 0, queue = 4 ):
        self.__listener = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
        self.__listener.bind( self.GetAddress() )
        self.__listener.setblocking( blocking )
        self.__listener.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
        self.__listener.listen( queue )
        self.__listener.settimeout( 5 )
        self.__read, self.__write, self.__error = [ self.__listener ], [], []

    def __AddClient( self, source ):
        c, a = source.accept()
        c.settimeout( 5 )
        self.__read.append( c )
        send( c, "Welcome!" )
        print a, "Connection established"
        return

    def __AddPlayer( self, source, nick ):
        if len( self.__players ) == 4:
            send( source, ('Error', "4 players already connected.") )
            self.__read.remove( source )
            return
        self.__players.append( nick )
        send( source, ('ID', self.__players.index(nick)) )

    def __RemovePlayer( self, source, gamer_id ):
        self.__players.pop( gamer_id )
        self.__read.remove( source )
        source.close()

    def __Connect( self ):
        joining = True
        while joining:
            r, w, x = select( self.__read, self.__write, self.__error, 0 )
            for s in r:
                if s is self.__listener:
                    self.__AddClient( s )
                else:
                    data = receive( s )
                    if data:
                        print data, s.getpeername()
                        if self.__MaintainPlayers( s, data ):
                            pass
            if len( self.__players ) == 4:
                joining = False
        return

    def __MaintainPlayers( self, source, data ):
        if data[0] == "Nick":
            self.__AddPlayer( source, data[1] )
            return True
        elif data[0] == "Quit":
            self.__RemovePlayer( source, data[1] )
            return True
        return False

    def run( self ):
        self.__SetSocket( 1, 4 )
        print "Waiting for players."
        self.__Connect()

其中,发送和接收功能如下:

def send( channel, message ):
    try:
        channel.send( json.dumps(message) )
        return True
    except OSError as e:
        print e
        return False

def receive( channel, packet_size = 64 ):
    try:
        data = channel.recv( int(packet_size) )
        if not data:
            return None
        print data
        return json.loads( data.strip() )
    except OSError as e:
        print e
        return False

client类很简单(编辑):

class client:
    def __init__( self, name, srvIP, srvPort ):
        ip = socket.gethostbyname( socket.gethostname() )
        self.__server_address = self.__server_ip, self.__server_port = srvIP, srvPort
        self.__ID = None
        self.__nick = name
        self.__SetListener()

    def __SetListener( self ):
        self.__listener = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
        self.__listener.settimeout( 5 )
        try:
            self.__listener.connect( self.__server_address )
        except Exception, e:
            print "Unable to connect", e
            raise e
        print "Connected to %s:%d." % self.__server_address
        send( self.__listener, ("Nick", self.__nick) )

    def run( self ):
        self.__read, self.__write, self.__error = [ self.__listener ], [], []
        while True:
            r, w, x = select( self.__read, self.__write, self.__error, 0 )
            for f in r:
                if f is self.__listener:
                    data = receive( f )
                    if data:
                        print data
                        if data[0] == "ID":
                            self.__ID = int( data[1] )
                        # More conditions

发生的情况是,我的客户端同时对receiveWelcome消息ID。这会引发异常,如下所示:

$ client.py
Connected to 10.109.1.92:7777.
"Welcome!"["ID", 0]
Traceback (most recent call last):
  File "%PATH%\client.py", line 115, in <module>
    c.run()
  File "%PATH%\client.py", line 86, in run
    data = receive( f )
  File "%PATH%\connect.py", line 17, in receive
    return loads( data.strip() )
  File "%PYTHON%\lib\json\__init__.py", line 338, in loads
    return _default_decoder.decode(s)
  File "%PYTHON%\lib\json\decoder.py", line 369, in decode
    raise ValueError(errmsg("Extra data", s, end, len(s)))
ValueError: Extra data: line 1 column 28 - line 1 column 37 (char 27 - 36)

也就是说,客户端收到以下字符串:

"Welcome!"["ID", 0]

会在json.loads中引发错误。

是否有一些方法可以在消息之间引入任何类型的延迟?

2 个答案:

答案 0 :(得分:4)

您需要在发送邮件时添加邮件的大小,以便在收到邮件时,您只能返回该邮件,并知道您拥有整条邮件。套接字模块中没有任何内容可以执行此操作,因为它们只是实现了低级管道。

当您使用消息的大小发送消息前缀时:

def send( channel, message ):
  try:
    msg = json.dumps(message)
    channel.send(struct.pack("i", len(msg)) + msg)
    return True
except OSError as e:
    print e
    return False

当您收到消息时,首先检索大小,然后重复调用recv,直到您收到整条消息。

def receive( channel ):
    try:
        size = struct.unpack("i", channel.recv(struct.calcsize("i")))[0]
        data = ""
        while len(data) < size:
            msg = channel.recv(size - len(data))
            if not msg:
                return None
            data += msg
        print data
        return json.loads( data.strip() )
    except OSError as e:
        print e
        return False

答案 1 :(得分:3)

或者,如果您可以保证您的消息不包含特定字符(例如空字节),您可以将其附加到您的字符串,然后服务器端,将该字符串拆分为空字符:

客户端:

socket.sendall(json_string + '\0')

服务器:

recv_buffer = ""
while True:
    data = connection.recv(128)
    recv_buffer = recv_buffer + data
    strings = recv_buffer.split('\0')
    for s in strings[:-1]:
        print("Received: %s" % s)
    recv_buffer = strings[-1]