python bloqueio soquetes, envie retorna imediatamente
-
19-09-2019 - |
Pergunta
Eu estou escrevendo um aplicativo de soquete multithread em Python utilizando o módulo socket. as escutas de servidor para conexões e quando chega um que gera um thread para esse soquete.
o fio servidor envia alguns dados para o cliente. mas o cliente ainda não está pronto para recebê-lo. Eu pensei que isso teria causado o servidor que esperar até que o cliente começa recv mas retorna imediatamente
O cliente então chama recv que está bloqueando e nenhum dado é sempre recebido.
cliente de soquete construtor
self.__clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.__clientSocket.connect((server, port))
servidor tomada construtor
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)
ouvir aceitar fio
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()
servidor e os clientes enviar / métodos rec de ClientSocket dentro
receivedData = self.__clientSocket.recv(1024*1024)
self.__clientSocket.send(s)
Todas as ideias por send () está retornando imediatamente?
Solução
Todas as ideias por send () está retornando imediatamente?
todos send () faz é preencher o buffer de rede e devolver o ammount de bytes enviados.
Se você quer um envio que bloqueia apenas recv uma mensagem de reconhecimento do cliente.
Outras dicas
O cliente não tem que estar pronto para receber dados - dados fila na do socket buffer de recebimento até que esteja pronto para recv ()-lo. Enviar retorna instantaneamente porque o buffer de envio não está cheio -. Se ele foi completo, send () iria bloquear até que não havia espaço para os dados que você queria enviar
Na maioria das vezes você nunca vai preenchê-lo - por isso que você está experimentando. De um lado, você provavelmente não quer uma chamada recv com resolução de 1024 * 1024 em que -. Que é um pouco no lado elevado
Desculpe a demora i corrigiu o problema pouco depois de fazer esta pergunta. @Lee obrigado pela sua resposta que me apontou na direção certa. a solução foi enviar um 4byte int especificar o tamanho dos dados a seguir. o cliente sempre receber estes quatro bytes e, em seguida, o tamanho dos dados.
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