【问题标题】:python blocking sockets, send returns immediatelypython阻塞套接字,立即发送返回
【发布时间】:2011-01-26 19:55:59
【问题描述】:

我正在使用 socket 模块在 Python 中编写一个多线程套接字应用程序。 服务器侦听连接,当它获得连接时,它为该套接字生成一个线程。

服务器线程向客户端发送一些数据。但是客户还没有准备好接收它。我认为这会导致服务器等到客户端启动 recv 而是立即返回

客户端然后调用recv,它被阻塞并且没有收到任何数据。

客户端套接字构造函数

self.__clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.__clientSocket.connect((server, port))

服务器套接字构造函数

        self.servSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.servSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        #self.servSock.settimeout(None)
        self.servSock.setblocking(1)
        self.servSock.bind((self.addr,self.port))
        self.servSock.listen(5)

监听接受线程

    try:
        (cs, address) = self.servSock.accept()
    except socket.timeout:
        return
    threadName = '\r\nClient %s:%s\r\n' % (cs, address)
    print threadName
    clientSocketHandler = ClientSocket()
    clientSocketHandler.setClientSocket(cs)
    self.clients.newThread(self.clientFunc, {clientSocketHandler : "1"}, threadName).start()

服务器和客户端从 ClientSocket 内部发送/接收方法

receivedData = self.__clientSocket.recv(1024*1024)

self.__clientSocket.send(s)

知道为什么 send() 会立即返回吗?

【问题讨论】:

    标签: python sockets multithreading blocking send


    【解决方案1】:

    知道为什么 send() 会立即返回吗?

    所有 send() 所做的就是填充网络缓冲区并返回发送的字节数。

    如果您想要阻止的发送,只需接收来自客户端的确认消息。

    【讨论】:

    • 那么我应该调用什么方法来进行阻塞发送?
    【解决方案2】:

    客户端不必准备好接收数据 - 数据将在套接字的接收缓冲区中排队,直到您准备好接收它。 Send 立即返回,因为发送缓冲区未满 - 如果它已满,send() 将阻塞,直到有空间容纳您要发送的数据。

    大多数时候你永远不会填满它 - 因此你正在经历什么。另一方面,您可能不希望其中包含 1024*1024 的 recv 调用 - 这有点偏高。

    【讨论】:

    • 我需要某种阻塞发送方法,在客户端收到它之前不会返回。我一直在尝试使用 sendall 并在发送后调用 recv,此时客户端将接收接收然后发送以解除阻塞
    【解决方案3】:

    抱歉,我在提出这个问题后不久就解决了这个问题。 @Lee 感谢您的回答,它为我指明了正确的方向。 解决方案是发送一个 4byte int 指定要遵循的数据大小。客户端总是会收到这四个字节,然后是数据的大小。

    from commandClass import Command
    from commandActionClass import CommandAction
    import socket
    from time import *
    import struct
    
    class ClientSocket():
        instance = None
        __connected = False
        __clientSocket = None
    
        @staticmethod
        def getInstance():
            if ClientSocket.instance == None:
                ClientSocket.instance = ClientSocket()
            return ClientSocket.instance
    
        def __init__(self):
            self.__connected = False
            self.receivedData = ''
            self.bufSize = 4096
            self.buffer = ''
    
        def connect(self, server, port):
            if self.isConnected():
                raise Exception('Already connected.')
    
            self.__clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.__clientSocket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
            self.__clientSocket.connect((server, port))
            self.__clientSocket.setblocking(1)
            self.__connected = True
    
        def disconnect(self):
            try:
                self.receivedData = ''
                self.buffer = ''
                self.__clientSocket.close()
            except Exception, e:
                print e
            finally:
                self.__connected = False
    
        def sendString(self,s):
            try:
                if (self.isConnected()):
                    self.__clientSocket.send(s)
            except Exception, e:
                print e
                self.disconnect()
    
        def __pop(self, size):
            data = self.receivedData[:size]
            self.receivedData = self.receivedData[size:]
            return data
    
        def __recv(self,toRead):
            self.flush()
            while ((len(self.receivedData)<toRead)and(self.isConnected())):
                data = self.__clientSocket.recv(self.bufSize)
                if not data:
                    self.disconnect()
                self.receivedData = self.receivedData + data
    
            return self.__pop(toRead)
    
        def __sendint(self, x):
            self.__sendall(struct.pack("i", x))
    
        def __recvint(self):
            data = self.__recv(4)
            if not data:
                raise Exception('Expected to receive buffer size')
            return struct.unpack("i", data)[0]
    
        def flush(self):
            if len(self.buffer)>0:
                self.__clientSocket.sendall(self.buffer)
            self.buffer = ''
    
        def __sendall(self, s):
            self.buffer = self.buffer + s
    
        def send(self,s):
            try:
                if (not self.isConnected()):
                    raise Exception('Socket is not connected')
                data = s.pickle()
                self.__sendint(len(data))
                self.__sendall(data)
            except Exception, e:
                self.disconnect()
                raise e
    
        def sendEOC(self):
            self.send(Command(CommandAction.EOC, time()))#send our system time. can be used for ping
    
        def receive(self):
            if (not self.isConnected()):
                raise Exception('Socket Error. Not Connected')
            try:
                #first receive the size of packet
                buffsize = self.__recvint()
                #now receive the actual data
                data = self.__recv(buffsize)
    
                if not data:
                    raise Exception('No data to receive')
    
                command = Command.unpickle(data)
            except Exception, e:
                self.disconnect()
                command = Command(CommandAction.Invalid, None)
                raise e
            #finally?
            return command
    
        def isConnected(self):
            return self.__connected
    
        def setClientSocket(self, clientSocket):
            self.__clientSocket = clientSocket
            self.__connected = True #assume its connected
    

    【讨论】:

    • 嗨,马克,你能举个例子说明你是如何做到的吗?我处于完全相同的位置:我试图从服务器向客户端发送一个冗长的字符串,但客户端只接收到其中的一小部分。如果您能证明这一点,将会非常有帮助。
    • @user690182 这是示例(我创建的类)
    猜你喜欢
    • 2013-06-04
    • 2012-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 2010-10-18
    • 1970-01-01
    • 2015-04-30
    相关资源
    最近更新 更多