with socket non how example español create close python networking timeout recv

non - Recepción de socket Python-los paquetes entrantes siempre tienen un tamaño diferente



socket python 3 español (6)

Estoy usando el módulo SocketServer para un servidor TCP. Estoy experimentando un problema aquí con la función recv() , porque los paquetes entrantes siempre tienen un tamaño diferente, así que si especifico recv(1024) (lo intenté con un valor más grande y más pequeño), se queda atascado después de 2 o 3 solicitudes porque la longitud del paquete será más pequeña (creo), y luego el servidor se queda bloqueado hasta que se agote el tiempo de espera.

class Test(SocketServer.BaseRequestHandler): def handle(self): print "From:", self.client_address while True: data = self.request.recv(1024) if not data: break if data[4] == "/x20": self.request.sendall("hello") if data[4] == "/x21": self.request.sendall("bye") else: print "unknow packet" self.request.close() print "Disconnected", self.client_address launch = SocketServer.ThreadingTCPServer(('''', int(sys.argv[1])),Test) launch.allow_reuse_address= True; launch.serve_forever()

Si el cliente envía solicitudes múltiples en el mismo puerto de origen, pero el servidor se queda atascado, cualquier ayuda sería muy apreciada, ¡gracias!


Alternativamente, puede usar recv(x_bytes, socket.MSG_WAITALL) , que parece funcionar solo en Unix, y devolverá exactamente x_bytes .


Esa es la naturaleza de TCP: el protocolo llena los paquetes (la capa más baja es paquetes IP) y los envía. Puede tener cierto grado de control sobre la MTU (Maximum Transfer Unit).

En otras palabras: debe diseñar un protocolo que se coloque sobre TCP donde se define su "delineación de carga útil". Por "delineación de la carga útil" me refiero a la forma en que extrae la unidad de mensaje que admite su protocolo. Esto puede ser tan simple como "cada cadena terminada en NULL".


La red siempre es impredecible. TCP hace que gran parte de este comportamiento aleatorio desaparezca para ti. Una cosa maravillosa que TCP hace: garantiza que los bytes llegarán en el mismo orden. ¡Pero! No garantiza que llegarán recortados de la misma manera. Simplemente no puede suponer que cada envío () desde un extremo de la conexión dará como resultado exactamente un recv () en el otro extremo con exactamente el mismo número de bytes.

Cuando dices socket.recv(x) , estás diciendo ''no socket.recv(x) hasta que hayas leído x bytes desde el socket''. Esto se denomina "E / S de bloqueo": bloqueará (esperará) hasta que se complete su solicitud. Si cada mensaje en su protocolo fuera exactamente 1024 bytes, llamar a socket.recv(1024) funcionaría muy bien. Pero parece que eso no es verdad. Si sus mensajes son un número fijo de bytes, simplemente pase ese número a socket.recv() y listo.

Pero, ¿y si tus mensajes pueden ser de diferentes longitudes? Lo primero que debe hacer es dejar de llamar a socket.recv() con un número explícito. Cambiando esto:

data = self.request.recv(1024)

a esto:

data = self.request.recv()

significa que recv() siempre regresará siempre que obtenga datos nuevos.

Pero ahora tiene un nuevo problema: ¿cómo sabe cuándo el remitente le envió un mensaje completo? La respuesta es: no lo haces. Tendrá que hacer que la longitud del mensaje sea una parte explícita de su protocolo. Esta es la mejor manera: prefija cada mensaje con una longitud, ya sea como un entero de tamaño fijo (convertido a orden de bytes de red utilizando socket.ntohs() o socket.ntohl() ¡por favor!) O como una cadena seguida de algún delimitador (como ''123:''). Este segundo enfoque a menudo es menos eficiente, pero es más fácil en Python.

Una vez que haya agregado eso a su protocolo, necesita cambiar su código para manejar recv() devolviendo cantidades arbitrarias de datos en cualquier momento. Aquí hay un ejemplo de cómo hacer esto. Intenté escribirlo como pseudocódigo o con comentarios para decirte qué hacer, pero no estaba muy claro. Así que lo escribí explícitamente usando el prefijo de longitud como una cadena de dígitos terminados por dos puntos. Aqui tienes:

length = None buffer = "" while True: data += self.request.recv() if not data: break buffer += data while True: if length is None: if '':'' not in buffer: break # remove the length bytes from the front of buffer # leave any remaining bytes in the buffer! length_str, ignored, buffer = buffer.partition('':'') length = int(length_str) if len(buffer) < length: break # split off the full message from the remaining bytes # leave any remaining bytes in the buffer! message = buffer[:length] buffer = buffer[length:] length = None # PROCESS MESSAGE HERE


La respuesta de Larry Hastings tiene algunos buenos consejos generales sobre los sockets, pero hay un par de errores en lo que respecta a cómo funciona el método recv(bufsize) en el módulo de socket de Python.

Por lo tanto, para aclarar, ya que esto puede ser confuso para otros que buscan ayuda:

  1. El parámetro bufsize para el método recv(bufsize) no es opcional. Obtendrá un error si llama a recv() (sin el param).
  2. El bufferlen en recv(bufsize) es un tamaño máximo . La restauración devolverá menos bytes si hay menos disponibles.

Ver docs.python.org/library/socket.html para más detalles.

Ahora, si está recibiendo datos de un cliente y desea saber cuándo recibió todos los datos, es probable que deba agregarlos a su protocolo, como sugiere Larry. Vea esta receta de estrategias para determinar el final del mensaje.

Como señala esa receta, para algunos protocolos, el cliente simplemente se desconectará cuando termine de enviar los datos. En esos casos, while True loop debería funcionar bien. Si el cliente no se desconecta, tendrá que encontrar la manera de señalar la duración de su contenido, delimitar sus mensajes o implementar un tiempo de espera.

Estaría encantado de intentar ayudar aún más si pudiera publicar su código de cliente exacto y una descripción de su protocolo de prueba.


Sé que esto es viejo, pero espero que esto ayude a alguien.

Utilizando zócalos normales de Python, descubrí que puedes enviar y recibir información en paquetes usando sendto y recvfrom

# tcp_echo_server.py import socket ADDRESS = '''' PORT = 54321 connections = [] host = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host.setblocking(0) host.bind((ADDRESS, PORT)) host.listen(10) # 10 is how many clients it accepts def close_socket(connection): try: connection.shutdown(socket.SHUT_RDWR) except: pass try: connection.close() except: pass def read(): for i in reversed(range(len(connections))): try: data, sender = connections[i][0].recvfrom(1500) return data except (BlockingIOError, socket.timeout, OSError): pass except (ConnectionResetError, ConnectionAbortedError): close_socket(connections[i][0]) connections.pop(i) return b'''' # return empty if no data found def write(data): for i in reversed(range(len(connections))): try: connections[i][0].sendto(data, connections[i][1]) except (BlockingIOError, socket.timeout, OSError): pass except (ConnectionResetError, ConnectionAbortedError): close_socket(connections[i][0]) connections.pop(i) # Run the main loop while True: try: con, addr = host.accept() connections.append((con, addr)) except BlockingIOError: pass data = read() if data != b'''': print(data) write(b''ECHO: '' + data) if data == b"exit": break # Close the sockets for i in reversed(range(len(connections))): close_socket(connections[i][0]) connections.pop(i) close_socket(host)

El cliente es similar

# tcp_client.py import socket ADDRESS = "localhost" PORT = 54321 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ADDRESS, PORT)) s.setblocking(0) def close_socket(connection): try: connection.shutdown(socket.SHUT_RDWR) except: pass try: connection.close() except: pass def read(): """Read data and return the read bytes.""" try: data, sender = s.recvfrom(1500) return data except (BlockingIOError, socket.timeout, AttributeError, OSError): return b'''' except (ConnectionResetError, ConnectionAbortedError, AttributeError): close_socket(s) return b'''' def write(data): try: s.sendto(data, (ADDRESS, PORT)) except (ConnectionResetError, ConnectionAbortedError): close_socket(s) while True: msg = input("Enter a message: ") write(msg.encode(''utf-8'')) data = read() if data != b"": print("Message Received:", data) if msg == "exit": break close_socket(s)


Tenga en cuenta que el motivo exacto por el que su código está congelado no se debe a que configure un tamaño de búfer request.recv () demasiado alto. Aquí se explica qué significa el tamaño del búfer en socket.recv (buffer_size)

Este código funcionará hasta que reciba un mensaje TCP vacío (si imprimiera este mensaje vacío, mostraría b'''' ):

while True: data = self.request.recv(1024) if not data: break

Y tenga en cuenta que no hay forma de enviar un mensaje TCP vacío. socket.send(b'''') simplemente no funcionará.

¿Por qué? Debido a que el mensaje vacío se envía solo cuando escribe socket.close() , su secuencia de comandos se repetirá siempre que no cierre su conexión. Como Hans L señaló aquí hay algunos buenos métodos para finalizar el mensaje .