Python bloquea sockets, envía devoluciones inmediatamente
-
19-09-2019 - |
Pregunta
Estoy escribiendo una aplicación de socket multiproceso en Python usando el módulo de socket.el servidor escucha las conexiones y cuando obtiene una genera un hilo para ese socket.
el hilo del servidor envía algunos datos al cliente.pero el cliente aún no está listo para recibirlo.Pensé que esto habría provocado que el servidor esperara hasta que el cliente iniciara la recepción, pero en su lugar regresaría inmediatamente.
Luego, el cliente llama a recv, que está bloqueando y nunca se reciben datos.
constructor de sockets de cliente
self.__clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.__clientSocket.connect((server, port))
constructor de sockets de servidor
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)
escuchando aceptar hilo
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()
métodos de envío/recepción del servidor y los clientes desde el interior de ClientSocket
receivedData = self.__clientSocket.recv(1024*1024)
self.__clientSocket.send(s)
¿Alguna idea de por qué send() regresa de inmediato?
Solución
alguna idea de por qué send () está regresando de inmediato?
toda send () hace es llenar el búfer de red y devolver el ammount de bytes enviados.
si desea un envío que bloquea simplemente recv un mensaje de reconocimiento por parte del cliente.
Otros consejos
El cliente no tiene que estar listo para recibir datos: los datos se pondrán en cola en el búfer de recepción del socket hasta que esté listo para recibirlos.El envío regresa instantáneamente porque el búfer de envío no está lleno; si estuviera lleno, send() se bloquearía hasta que hubiera espacio para los datos que desea enviar.
La mayoría de las veces nunca lo llenarás, de ahí lo que estás experimentando.Por un lado, probablemente no quieras una llamada de recepción con 1024*1024; eso es un poco alto.
Lo siento por el retraso i fijo el problema poco después de hacer esta pregunta. @Lee gracias por su respuesta que me señaló en la dirección correcta. la solución era enviar un 4byte int especificando el tamaño de los datos a seguir. el cliente siempre recibiría estos cuatro bytes y luego el tamaño de los datos.
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