Código Python para Cliente de Registro, Conexión y Transferencia de Archivos P2P

Informática10,62 KB

PYTHON

Función register

def register(user):
try:
# conectar, enviar REGISTER, nombre y leer la respuesta del servidor
s = client.connect_to_server()
client._send_str(s, "REGISTER")
client._send_str(s, user)
# leer la respuesta del servidor
cod = client._recv_byte(s)
s.close()
except Exception as e:
print("c> REGISTER FAIL")
print(f"Motivo real del fallo: {e}")
return client.RC.ERROR
# procesar el código de respuesta del servidor
if cod == 0:
client._registered_user = user # guarda el estado
print("c> REGISTER OK")
return client.RC.OK
elif cod == 1:
print("c> USERNAME IN USE")
return client.RC.USER_ERROR
else:
print("c> REGISTER FAIL")
return client.RC.ERROR

Función unregister

def unregister(user):
try:
s = client._connect_to_server()
client._send_str(s, "UNREGISTER")
client._send_str(s, user)
cod = client._recv_byte(s)
s.close()
except Exception:
print("c> UNREGISTER FAIL")
return client.RC.ERROR
if cod == 0:
client._registered_user = None
print("c> UNREGISTER OK")
return client.RC.OK
elif cod == 1:
print("c> USER DOES NOT EXIST")
return client.RC.USER_ERROR
else:
print("c> UNREGISTER FAIL")
return client.RC.ERROR

Función connect

def connect(user):
# para evitar doble conexión en el mismo cliente
if client.connected_user is not None:
print("c> USER ALREADY CONNECTED")
return client.RC.USER_ERROR
# buscamos un puerto libre y levantamos el hilo de escucha antes de enviar el comando CONNECT al servidor
# para asegurarnos de que el cliente ya está escuchando cuando el servidor le envíe mensajes entrantes
listen_port = client._find_free_port()
# creamos un hilo secundario que ejecuta el listener y le pasamos el puerto encontrado
# daemon=True: el hilo muere automáticamente si cerramos el programa principal
t = threading.Thread(target=client._listener_thread_func, args=(listen_port,), daemon=True)
t.start() # arrancamos el hilo --> cliente escuchando en segundo plano
try:
s = client.connect_to_server() # abrimos socket TCP al servidor
# enviamos la petición: operación --> usuario --> puerto de escucha
client.send_str(s, "CONNECT")
client.send_str(s, user)
client._send_str(s, str(listen_port)) # informamos al servidor por dónde escuchamos
# esperamos que el servidor responda con 1 byte para error o éxito
cod = client._recv_byte(s)
s.close()
except Exception:
# si falla matamos el socket que habíamos abierto para el hilo de escucha
if client.listen_sock is not None:
client._listen_sock.close()
client.listen_sock = None
print("c> CONNECT FAIL")
return client.RC.ERROR
if cod == 0:
client._connected_user = user # guardamos el usuario para no tener que escribirlo en cada print
print("c> CONNECT OK")
return client.RC.OK
# gestión de códigos de error
elif cod == 1:
if client.listen_sock is not None:
client.listen_sock.close()
client.listen_sock = None
print("c> CONNECT FAIL, USER DOES NOT EXIST")
return client.RC.USER_ERROR
elif cod == 2:
if client.listen_sock is not None:
client._listen_sock.close()
client._listen_sock = None
print("c> USER ALREADY CONNECTED")
return client.RC.USER_ERROR
else:
if client.listen_sock is not None:
client.listen_sock.close()
client.listen_sock = None
print("c> CONNECT FAIL")
return client.RC.ERROR

Función users

def users(silent=False):
if client._connected_user is None:
if not silent:
print("c> CONNECTED USERS FAIL, USER IS NOT CONNECTED")
return client.RC.USER_ERROR
try:
s = client.connect_to_server()
client.send_str(s, "USERS")
client.send_str(s, client._connected_user)
cod = client._recv_byte(s)
if cod == 0:
# leemos cuántos usuarios va a mandar el servidor
n_str = client.recv_str(s)
n = int(n_str)
# vaciar la caché antigua
client._peer_info.clear()
nombres_limpios = []
# leemos la lista y separamos usuario:ip:puerto
for _ in range(n):
data = client._recv_str(s)
partes = data.split(":")
if len(partes) >= 3:
nombres_limpios.append(partes[0])
# guardar en diccionarios para transferencias P2P
client._peer_info[partes[0]] = (partes[1], int(partes[2]))
else:
nombres_limpios.append(data)
s.close()
# imprimir por pantalla
if not silent:
print(f"c> CONNECTED USERS ({n} users connected) OK")
for nombre in nombres_limpios:
ip, puerto = client._peer_info[nombre]
print(f"{nombre} :: {ip} :: {puerto}")
return client.RC.OK
else:
# gestión de errores
s.close()
if cod == 1:
if not silent:
print("c> CONNECTED USERS FAIL, USER IS NOT CONNECTED")
return client.RC.USER_ERROR
else:
if not silent:
print("c> CONNECTED USERS FAIL")
return client.RC.ERROR
except Exception:
if not silent:
print("c> CONNECTED USERS FAIL")
return client.RC.ERROR

Función disconnect

def disconnect(user):
try:
# avisar al servidor para que marque como desconectado
s = client._connect_to_server()
client._send_str(s, "DISCONNECT")
client._send_str(s, user)
cod = client._recv_byte(s)
s.close()
except Exception:
cod = 3
# paramos el hilo cerrando el socket de escucha directamente
try:
if client._listen_sock is not None:
client._listen_sock.shutdown(socket.SHUT_RDWR)
client._listen_sock.close()
client._listen_sock = None
except Exception:
pass
client.connected_user = None
if cod == 0:
print("c> DISCONNECT OK")
return client.RC.OK
# gestión de errores
elif cod == 1:
print("c> DISCONNECT FAIL, USER DOES NOT EXIST")
return client.RC.USER_ERROR
elif cod == 2:
print("c> DISCONNECT FAIL, USER NOT CONNECTED")
return client.RC.USER_ERROR
else:
print("c> DISCONNECT FAIL")
return client.RC.ERROR

Función send

def send(user, message):
# antes de enviar el mensaje llamamos a la función auxiliar que utiliza zeep
# se conecta al puerto --> le pasa el mensaje --> devuelve el mensaje sin espacios en blanco
message = client._normalize_message(message)
# validar tamaños máximos
if len(message) > 255 or len(user) > 255:
print("c> SEND FAIL")
return client.RC.ERROR
if client._connected_user is None:
print("c> SEND FAIL")
return client.RC.ERROR
# enviar la petición al servidor central
try:
s = client.connect_to_server()
# operación --> remitente --> destinatario --> mensaje
client.send_str(s, "SEND")
client.send_str(s, client.connected_user)
client._send_str(s, user)
client._send_str(s, message)
cod = client._recv_byte(s) # espera el código de estado (0=ok, 1=user error, 2=error general)
if cod == 0:
# si todo va bien el servidor devuelve el ID del mensaje, leemos y lo imprimimos
msg_id = client._recv_str(s)
s.close()
print(f"c> SEND OK MESSAGE {msg_id}")
return client.RC.OK
else:
# códigos de error
s.close()
if cod == 1:
print("c> SEND FAIL, USER DOES NOT EXIST")
return client.RC.USER_ERROR
else:
print("c> SEND FAIL")
return client.RC.ERROR
except Exception:
print("c> SEND FAIL")
return client.RC.ERROR

Función sendAttach

def sendAttach(user, file, message):
# normalizar el mensaje con el Servicio Web
message = client._normalize_message(message)
# validar tamaños máximos (255 caracteres + 1 byte para \0 = 256)
if len(message) > 255 or len(file) > 255 or len(user) > 255:
print("c> SENDATTACH FAIL")
return client.RC.ERROR
if client._connected_user is None:
print("c> SENDATTACH FAIL")
return client.RC.ERROR
try:
s = client._connect_to_server()
client._send_str(s, "SENDATTACH")
client._send_str(s, client._connected_user)
client._send_str(s, user)
client._send_str(s, message)
client._send_str(s, file)
cod = client._recv_byte(s)
if cod == 0:
msg_id = client._recv_str(s)
s.close()
print(f"c> SENDATTACH OK MESSAGE {msg_id}")
return client.RC.OK
else:
s.close()
if cod == 1:
print("c> SENDATTACH FAIL, USER DOES NOT EXIST")
return client.RC.USER_ERROR
else:
print("c> SENDATTACH FAIL")
return client.RC.ERROR
except Exception:
print("c> SENDATTACH FAIL")
return client.RC.ERROR

Función getFile

def getFile(user, remote_file, local_file):
# comprobar si tenemos la info en caché; si no, refrescar de forma silenciosa
if user not in client._peer_info:
client.users(silent=True)
# si sigue sin existir, el usuario no está conectado
if user not in client._peer_info:
print("c> FILE TRANSFER FAILED, user not connected.")
return client.RC.ERROR
# sacamos la IP y puerto del usuario
ip, port = client._peer_info[user]
try:
# conexión P2P directa
peer_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
peer_sock.connect((ip, port))
# protocolo GET FILE --> usuario --> qué fichero quiere
client.send_str(peer_sock, "GET FILE")
client._send_str(peer_sock, client._connected_user)
client._send_str(peer_sock, remote_file)
# descarga del archivo en bucle
# abrimos fichero local en modo escritura binaria (wb)
with open(local_file, "wb") as f:
while True:
chunk = peer_sock.recv(4096) # leemos 4 KB para no saturar la memoria
if not chunk:
break # el otro lado cerró la conexión al terminar
f.write(chunk) # escribir en disco lo que recibimos por el socket
peer_sock.close()
# imprimir un mensaje de éxito local
print(f"c> GETFILE OK. Archivo guardado como {local_file}")
return client.RC.OK
except Exception as e:
# si falla la conexión --> la IP era antigua --> limpiamos la caché
client._peer_info.pop(user, None)
print("c> FILE TRANSFER FAILED, user not connected.")
return client.RC.ERROR

Descargar

PDF Word HTML