简体   繁体   English

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

[英]Client receiving two separate message as one with python socket

I define the server class as follows ( redacted ):我将服务器类定义如下(已编辑):

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()

where, the send and receive functions are as follows:其中,发送和接收函数如下:

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

The client class is pretty simple ( redacted ): 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

What happens is, my client objects receive the Welcome and ID message simultaneously.发生的情况是,我的客户端对象同时receive WelcomeID消息。 This throws an exception as follows:这将引发如下异常:

$ 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)

That is, the client receives the following as a single string:也就是说,客户端将以下内容作为单个字符串接收:

 "Welcome!"["ID", 0]

which raises an error in json.loads .这会在json.loads引发错误。

Is there some method to introduce any sort of delay between the messages?是否有某种方法可以在消息之间引入任何类型的延迟?

Alternatively, if you can guarantee your message won't contain a specific character (eg a null byte), you can append that to your string, and then server side, split the string on the null character:或者,如果您可以保证您的消息不包含特定字符(例如空字节),您可以将其附加到您的字符串,然后服务器端,在空字符上拆分字符串:

Client:客户:

socket.sendall(json_string + '\0')

Server:服务器:

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]

You need to add the size of the message when you send it so that when you receive it you can only return that message, and know that you have the whole message.您需要在发送消息时添加消息的大小,以便在您收到消息时只能返回该消息,并且知道您拥有完整的消息。 There is nothing in the socket module to do this as they just implement a low level pipe. socket 模块中没有任何东西可以做到这一点,因为它们只是实现了一个低级管道。

When you send the message prefix it with the size of the message:当您发送消息时,使用消息的大小作为前缀:

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

When you receive the message first retrieve the size, then repeatedly call recv until you have the whole message.当您收到消息时首先检索大小,然后反复调用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.decode('utf-8')
        print data
        return json.loads( data.strip() )
    except OSError as e:
        print e
        return False

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM