El socket de Python no recibe sin enviar

Resuelto no0by5 asked hace 7 años • 2 respuestas

Copié el ejemplo del servidor echo de la documentación de Python y está funcionando bien. Pero cuando edito el código, para que no envíe los datos al cliente, el método socket.recv() no regresa cuando se llama por segunda vez.

import socket

HOST = ''
PORT = 50007 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)

conn, addr = s.accept()
print('Connected by', addr)

while True:
    data = conn.recv(1024)
    if not data: break
conn.sendall(b'ok')
conn.close()

En la versión original de la documentación de Python, el bucle while es ligeramente diferente:

while True:
    data = conn.recv(1024)
    if not data: break
    conn.sendall(data)

Código del cliente:

import socket

HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)
s.close()
print('Received', repr(data))
no0by5 avatar Apr 15 '17 05:04 no0by5
Aceptado

Los sockets TCP son flujos de datos. No existe una correlación uno a uno entre enviar llamadas por un lado y recibir llamadas por el otro. Existe una correlación de nivel superior según el protocolo que implemente. En el código original, la regla era que el servidor enviaría exactamente lo que recibía hasta que el cliente cerrara el lado entrante de la conexión. Luego el servidor cerró el socket.

Con tu cambio, las reglas cambiaron. Ahora el servidor sigue recibiendo y descartando datos hasta que el cliente cierra el lado entrante de la conexión. Luego el servidor envía "ok" y cierra el socket.

Un cliente que utiliza la primera regla se bloquea porque espera datos antes de cerrar el socket. Si quiere trabajar con esta nueva regla del servidor, tiene que cerrar el lado saliente del socket para indicarle al servidor que ha terminado y luego podrá obtener los datos de retorno.

Actualicé el cliente y el servidor para cerrar partes de la conexión y también pedí que el cliente realice varias recepciones en caso de que los datos entrantes estén fragmentados. Las implementaciones menos completas parecen funcionar para cargas útiles pequeñas porque es poco probable que se produzca fragmentación, pero se rompen horriblemente en el código de producción real.

servidor

import socket

HOST = ''
PORT = 50007 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(1)

conn, addr = s.accept()
print('Connected by', addr)

while True:
    data = conn.recv(1024)
    if not data: break
conn.sendall(b'ok')
conn.shutdown(socket.SHUT_WR)
conn.close()

cliente

import socket

HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
s.shutdown(socket.SHUT_WR)
data = b''
while True:
    buf = s.recv(1024)
    if not buf:
        break
    data += buf
s.close()
print('Received', repr(data))
tdelaney avatar Apr 14 '2017 23:04 tdelaney

El número de operaciones de recepción y envío debe coincidir porque están bloqueando. Este es el diagrama de flujo de su código:

Server listen 
Client connect 
Server receive (this waits until a message arrives at the server) [1] 
Client send 'Hello world' (received by [1])
Server receive (because there was data received) [2]
Client receive [3]

Debido a que el servidor y el cliente están bloqueados ahora, ningún programa puede continuar.

La solución sería eliminar la llamada de recepción del cliente porque eliminó la llamada de envío del servidor.

Sekuraz avatar Apr 14 '2017 22:04 Sekuraz