блокирующие сокеты python, отправка немедленно возвращается

Я пишу приложение многопоточного сокета на 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 () сразу возвращается?


person Mark    schedule 12.03.2010    source источник


Ответы (3)


есть идеи, почему send () сразу возвращается?

все, что делает send (), - это заполняет сетевой буфер и возвращает количество отправленных байтов.

если вы хотите, чтобы отправка, которая блокировала, просто получала подтверждающее сообщение от клиента.

person Hortinstein    schedule 12.03.2010
comment
Итак, какой метод я вызываю, чтобы выполнить блокирующую отправку? - person Mark; 12.03.2010

Клиент не должен быть готов к приему данных - данные будут стоять в очереди в приемном буфере сокета, пока вы не будете готовы к их вызову recv (). Send возвращается мгновенно, потому что буфер отправки не заполнен - ​​если он был заполнен, send () блокировался бы до тех пор, пока не было места для данных, которые вы хотели отправить.

В большинстве случаев вы никогда не заполните его - отсюда и то, что вы испытываете. С другой стороны, вам, вероятно, не нужен recv-вызов с разрешением 1024 * 1024 - это немного выше.

person Lee    schedule 12.03.2010
comment
Мне нужен какой-то блокирующий метод отправки, который не вернется, пока клиент его не получит. Я экспериментирую с sendall и вызываю recv после отправки, после чего клиент будет получать, а затем отправлять, чтобы разблокировать - person Mark; 13.03.2010

Извините за задержку, я решил проблему вскоре после того, как задал этот вопрос. @Lee спасибо за ваш ответ, он указал мне в правильном направлении. решение заключалось в том, чтобы отправить 4-байтовое 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
person Mark    schedule 03.05.2010
comment
Привет, Марк, не могли бы вы привести пример того, как вы заставили это работать? Я нахожусь в том же положении: я пытаюсь отправить длинную строку с сервера клиенту, но клиент получает лишь небольшую ее часть. Было бы очень полезно, если бы вы это продемонстрировали. - person user690182; 23.12.2011
comment
@ user690182 вот пример (созданный мной класс) - person Mark; 02.02.2012