uni-lab/clab/client-server-system/client/client.py
2026-03-08 18:57:07 +01:00

391 lines
15 KiB
Python
Executable file

#!/usr/bin/env python3
from utils import *
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTSTP, signal.SIG_IGN)
# --------------------------------------------------------------
# --------------------- FASE DE REGISTRE -----------------------
# --------------------------------------------------------------
def register(sock, server_address):
global client
client.register_attemps += 1
logger.debug_print(f"Registrant el client al servidor. (Intent {client.register_attemps})")
if client.register_attemps > o:
logger.debug_print("S'ha superat el nombre d'intents permesos")
print("No s'ha pogut contactar amb el servidor")
client.current_state = DISCONNECTED
else:
send_reg_req(sock, server_address, 0, t)
def send_reg_req(sock, server_address, num_sends, timeout):
global client
if num_sends >= n:
time.sleep(u)
register(sock, server_address)
elif not client.has_state(REGISTERED):
if num_sends >= p and timeout < q * t:
timeout += t
sendto(sock, server_address, REG_REQ,
client.id, "00000000\0", "")
client.current_state = WAIT_ACK_REG
(inputs, _, _) = select.select([sock], [], [], timeout)
if len(inputs) > 0:
response, addr = recvfrom(sock)
if client.has_state(WAIT_ACK_REG) and addr[0] == server_address[0]:
pck = response[0]
if pck == REG_ACK:
client.set_udp_communication_port(int(response[3]))
client.set_server_credentials(
response[1], response[2], addr[0])
send_info(sock, server_address, num_sends, timeout)
elif pck == REG_NACK:
client.current_state = NOT_REGISTERED
send_reg_req(sock, server_address, num_sends + 1, timeout)
else:
client.current_state = NOT_REGISTERED
register(sock, server_address)
else:
client.current_state = NOT_REGISTERED
register(sock, server_address)
else:
send_reg_req(sock, server_address, num_sends + 1, timeout)
def send_info(sock, server_address, num_reg_sends, reg_timeout):
global client
info_timeout = t * 2
address = (client.server_ip, client.udp_port)
data = f"{str(client.local_tcp)},{';'.join(list(client.elems))}"
sendto(sock, address, REG_INFO, client.id, client.rand_num, data)
client.current_state = WAIT_ACK_INFO
(inputs, _, _) = select.select([sock], [], [], info_timeout)
if len(inputs) > 0:
response, addr = recvfrom(sock)
pck = response[0]
if client.has_state(WAIT_ACK_INFO) and client.check_server_credentials(server_id=response[1],
rand_num=response[2],
server_ip=addr[0]):
if pck == INFO_ACK:
client.set_tcp_communication_port(int(response[3]))
client.current_state = REGISTERED
elif pck == INFO_NACK:
client.current_state = NOT_REGISTERED
send_reg_req(sock, server_address,
num_reg_sends + 1, reg_timeout)
else:
client.current_state = NOT_REGISTERED
register(sock, server_address)
else:
client.current_state = NOT_REGISTERED
register(sock, server_address)
else:
client.current_state = NOT_REGISTERED
register(sock, server_address)
# --------------------------------------------------------------
# --------------------- FASE DE COMUNICACIÓ --------------------
# ---------------------------PERIÒDICA--------------------------
# --------------------------------------------------------------
def recv_first_alive(sock):
global client
timeout = r * v
(inputs, _, _) = select.select([sock], [], [], timeout)
if len(inputs) > 0:
response, server_addr = recvfrom(sock)
pck, client_id = response[0], response[-1]
if client.check_server_credentials(server_id=response[1], rand_num=response[2],
server_ip=server_addr[0]) and client_id == client.id:
if pck == ALIVE:
client.current_state = SEND_ALIVE
else:
client.current_state = NOT_REGISTERED
else:
client.current_state = NOT_REGISTERED
else:
client.current_state = NOT_REGISTERED
def recv_alive(sock):
global client
alives_not_received = 0
max_lost_alive = s
timeout = r * v
while client.has_state(SEND_ALIVE):
(inputs, _, _) = select.select([sock], [], [], timeout)
if len(inputs) > 0:
response, server_addr = recvfrom(sock)
pck, client_id = response[0], response[-1]
if client.check_server_credentials(server_id=response[1], rand_num=response[2],
server_ip=server_addr[0]) and client_id == client.id:
if pck == ALIVE:
alives_not_received = 0
else:
client.current_state = NOT_REGISTERED
else:
client.current_state = NOT_REGISTERED
else:
alives_not_received += 1
if alives_not_received >= max_lost_alive:
logger.debug_print(
f"No s'han rebut {max_lost_alive} paquets ALIVE consecutius, estat actual: NOT_REGISTERED")
client.current_state = NOT_REGISTERED
def send_alive(sock, server_udp_address):
global client
timeout = v
while client.has_state(REGISTERED, SEND_ALIVE):
sendto(sock, server_udp_address, ALIVE, client.id, client.rand_num, "")
time.sleep(timeout)
# --------------------------------------------------------------
# --------------------- REBUDA DE COMANDES ---------------------
# --------------------------------------------------------------
def start_cli():
global client
print("\nBenvingut, introdueix 'help' per veure les comandes acceptades.")
while client.has_state(SEND_ALIVE):
try:
cmd = input().strip().split()
if len(cmd) == 0:
pass
elif cmd[0] == 'help':
help()
elif cmd[0] == 'stat':
run_stat()
elif cmd[0] == 'set':
run_set(cmd)
elif cmd[0] == 'send':
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((client.server_ip, client.tcp_port))
run_send(sock, cmd)
sock.close()
except ConnectionError as err:
print(
f"Connexió rebutjada, pot ser que el servidor estigui desconnectat.")
pass
elif cmd[0] == 'quit':
client.current_state = DISCONNECTED
else:
print("\tComanda no acceptada.")
except EOFError:
os.kill(os.getpid(), signal.SIGINT)
def help():
print("\tstat")
print("\tset <identificador_element> <nou_valor>")
print("\tsend <identificador_element>")
print("\tquit")
def run_stat():
global client
print(" ---ELEMS---|--VALUE---------")
for key, value in client.elems.items():
print(f"\t{key} : {value}")
def run_set(cmd):
if len(cmd) != 3:
print("\tset <identificador_element> <nou_valor>")
elif cmd[1] not in client.elems:
print("\tL'element no existeix")
else:
elem_id = cmd[1]
new_value = cmd[2]
client.elems[elem_id] = new_value
def run_send(sock, cmd):
if len(cmd) != 2:
print("\tsend <identificador_element>")
elif cmd[1] not in client.elems:
print("\tL'element no existeix")
else:
timeout = m
elem = cmd[1]
value = client.elems[elem]
info = get_actual_date()
send(sock, SEND_DATA, client.id, client.rand_num, elem, value, info)
(inputs, _, _) = select.select([sock], [], [], timeout)
if len(inputs) > 0:
response = recv(sock)
if response is not None:
pck = response[0]
if client.check_server_credentials(server_id=response[1], rand_num=response[2]):
if pck == DATA_ACK:
client_id = response[-1]
if client_id == client.id:
logger.debug_print("\tDades acceptades!")
else:
logger.debug_print("\tDades d'identificació del client rebudes erronees, client passa a l'estat NOT_REGISTERED")
client.current_state = NOT_REGISTERED
elif pck == DATA_NACK:
logger.debug_print(
f"\tDades no acceptades, motiu: {response[-1]}")
else:
logger.debug_print(
f"\tDades rebutjades per part del servidor")
client.current_state = NOT_REGISTERED
else:
logger.debug_print("\tDades d'identificació del servidor rebudes erronees, client passa a l'estat NOT_REGISTERED")
client.current_state = NOT_REGISTERED
else:
logger.debug_print("\tDades d'identificació del servidor rebudes erronees, client passa a l'estat NOT_REGISTERED")
client.current_state = NOT_REGISTERED
else:
logger.debug_print("\tNo ha hagut una resposta per part del servidor")
def get_actual_date():
date = datetime.datetime.now()
info = f'{str(date.year)}-{str(date.month)}-{str(date.day)};{str(date.hour)}:{str(date.minute)}:{str(date.second)}'
return info
# --------------------------------------------------------------
# --------------------- ESPERA DE CONNEXIONS--------------------
# --------------------------------------------------------------
def wait_server_connections(sock):
while client.has_state(SEND_ALIVE):
(inputs, _, _) = select.select([sock], [], [], 0.0)
if len(inputs) > 0:
new_sock, addr = sock.accept()
response = recv(new_sock)
if response is not None:
pck, client_id, elem = response[0], response[-1], response[3]
if client.check_server_credentials(server_id=response[1], rand_num=response[2]) and client_id == client.id:
if elem not in client.elems:
send(new_sock, DATA_NACK, client.id,
client.rand_num, elem, "", "Element no trobat")
if pck == SET_DATA:
value = response[4]
set_data(new_sock, elem, value)
elif pck == GET_DATA:
get_data(new_sock, elem)
else:
send(new_sock, DATA_REJ, client.id, client.rand_num, elem, "",
"Hi ha discrepancies en les dades del servidor i/o del dispositiu")
client.current_state = NOT_REGISTERED
else:
send(new_sock, DATA_REJ, client.id, client.rand_num, elem, "",
"Les dades rebudes estan mal formades")
client.current_state = NOT_REGISTERED
new_sock.close()
sock.close()
def set_data(sock, elem, value):
global client
if elem[-1] != 'I':
send(sock, DATA_NACK, client.id, client.rand_num,
elem, "", "Tipus de l'element incorrecte")
else:
client.elems[elem] = value
logger.debug_print(
f"Valor {value} assignat a l'element {elem} per el servidor")
send(sock, DATA_ACK, client.id,
client.rand_num, elem, value, client.id)
def get_data(sock, elem):
logger.debug_print(
f"Dades de l'element {elem} demanades per el servidor.")
value = client.elems[elem]
send(sock, DATA_ACK, client.id,
client.rand_num, elem, value, client.id)
# ---------------------------------------------------------------
# ------------------------THREADS CREATION-----------------------
# ---------------------------------------------------------------
def run_send_alive(udp_sock, server_address):
s_alive = threading.Thread(target=send_alive, args=[
udp_sock, server_address], daemon=True)
s_alive.start()
def run_recv_alive(udp_sock):
r_alive = threading.Thread(target=recv_alive, args=[udp_sock], daemon=True)
r_alive.start()
def run_cli():
cli = threading.Thread(target=start_cli, daemon=True)
cli.start()
def run_connections(sock):
wait_connections = threading.Thread(
target=wait_server_connections, args=[sock], daemon=True)
wait_connections.start()
# ---------------------------------------------------------------
# ------------------------MAIN BLOCK-----------------------------
# ---------------------------------------------------------------
# Creem una instancia de la classe logger
# que ens permetrà printar missatges de debug
logger = Logger()
cfgname = "client.cfg"
# Analitzem els arguments
# introduits per el script
if len(sys.argv) > 1:
option = sys.argv[1]
if option == '-c':
if len(sys.argv) != 3:
print(f"{sys.argv[0]} {sys.argv[1]} <filename>")
cfgname = sys.argv[2]
elif option == '-d':
logger.turn_debug_on()
# Creem una instancia de la classe Client
# que ens ajudarà a gestionar variables com el estat
# del client i la seva configuració
client = Client(cfgname)
try:
server_udp_address = (client.server, client.server_udp)
udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_sock.bind(client.local_address())
while not client.has_state(DISCONNECTED):
if client.has_state(NOT_REGISTERED):
# Iniciem la fase de registre
register(udp_sock, server_udp_address)
if client.has_state(REGISTERED):
logger.debug_print("Enviant paquets ALIVE al servidor")
# Comencem a enviar paquets ALIVE al servidor
run_send_alive(udp_sock, server_udp_address)
# Esperem el primer paquet alive
recv_first_alive(udp_sock)
if client.has_state(SEND_ALIVE):
logger.debug_print("Primer paquet ALIVE rebut")
logger.debug_print(
"Registre finalitzat, estat actual: SEND_ALIVE")
# Iniciem la rebuda de ALIVEs del servidor
run_recv_alive(udp_sock)
# Obrim el port tcp local per a les connexions amb el servidor
tcp_sock = socket.socket(
socket.AF_INET, socket.SOCK_STREAM)
tcp_sock.bind(('', client.local_tcp))
tcp_sock.listen()
# Obrim la consola de comandes
run_cli()
# Iniciem la espera de connexions amb el servidor
run_connections(tcp_sock)
except OSError as err:
print(f"OSError: {err}")
finally:
# Realitzem un exit el qual tanca tots els
# descriptors de fitxer relacionats amb el procés
sys.exit(0)