feat(udp): help functionality finished

- Added res-code to client.py
- Improved client error handling
This commit is contained in:
minhtrannhat 2023-12-01 04:04:18 -05:00
parent 236853828f
commit d369b8e1b1
Signed by: minhtrannhat
GPG Key ID: E13CFA85C53F8062
2 changed files with 86 additions and 24 deletions

View File

@ -19,11 +19,20 @@ summary_command_pattern: Pattern = re.compile(r"^summary\s+[^\s]+$")
change_command_pattern: Pattern = re.compile(r"^change\s+[^\s]+\s+[^\s]+$") change_command_pattern: Pattern = re.compile(r"^change\s+[^\s]+\s+[^\s]+$")
# opcodes # opcodes
put_request_opcode = "000" put_request_opcode:str = "000"
get_request_opcode = "001" get_request_opcode:str = "001"
change_request_opcode = "010" change_request_opcode: str = "010"
summary_request_opcode = "011" summary_request_opcode: str = "011"
help_requrest_opcode = "100" help_requrest_opcode: str = "100"
# Res-code
correct_put_and_change_request_rescode: str = "000"
correct_get_request_rescode: str = "001"
correct_summary_request_rescode: str = "010"
file_not_error_rescode: str = "011"
unknown_request_rescode: str = "100"
unsuccessful_change_rescode: str = "101"
help_rescode: str = "110"
# custome type to represent the hostname(server name) and the server port # custome type to represent the hostname(server name) and the server port
Address = Tuple[str, int] Address = Tuple[str, int]
@ -64,7 +73,10 @@ class UDPClient:
# help # help
elif command == "help": elif command == "help":
continue request_payload: str = help_requrest_opcode + "00000"
print(
f"myftp> - {self.mode} - : asking for help from the server"
) if self.debug else None
# get command handling # get command handling
elif get_command_pattern.match(command): elif get_command_pattern.match(command):
@ -87,7 +99,7 @@ class UDPClient:
f"myftp> - {self.mode} - : summary file {filename} from the server" f"myftp> - {self.mode} - : summary file {filename} from the server"
) if self.debug else None ) if self.debug else None
# summary command handling # change command handling
elif change_command_pattern.match(command): elif change_command_pattern.match(command):
_, old_filename, new_filename = command.split() _, old_filename, new_filename = command.split()
print( print(
@ -100,9 +112,10 @@ class UDPClient:
) )
continue continue
client_socket.send(command.encode()) client_socket.send(request_payload.encode("utf-8"))
modified_message = client_socket.recv(2048) modified_message = client_socket.recv(2048)[1:]
print(modified_message.decode()) print(modified_message.decode())
client_socket.close() # type: ignore
except ConnectionRefusedError: except ConnectionRefusedError:
print( print(
@ -112,8 +125,6 @@ class UDPClient:
print( print(
f"myftp> - {self.mode} - {error} happened." f"myftp> - {self.mode} - {error} happened."
) )
finally:
client_socket.close() # type: ignore
# ping pong UDP # ping pong UDP
def check_udp_server(self): def check_udp_server(self):
@ -170,9 +181,7 @@ def get_address_input() -> Address:
# Ensure there are exactly two parts # Ensure there are exactly two parts
if len(input_parts) != 2: if len(input_parts) != 2:
raise ValueError( raise ValueError
"myftp>Invalid input. Please enter a servername/hostname/ip address as a string and the port number as an integer separated by a space."
)
# Extract the values and create the tuple # Extract the values and create the tuple
string_part, int_part = input_parts string_part, int_part = input_parts
@ -183,7 +192,7 @@ def get_address_input() -> Address:
except ValueError as e: except ValueError as e:
print( print(
f"Error: {e}. Invalid input. Please enter a servername/hostname/ip address as a string and the port number as an integer separated by a space." f"Error: Invalid input. Please enter a servername/hostname/ip address as a string and the port number as an integer separated by a space."
) )

View File

@ -17,6 +17,13 @@ unknown_request_rescode: str = "100"
unsuccessful_change_rescode: str = "101" unsuccessful_change_rescode: str = "101"
help_rescode: str = "110" help_rescode: str = "110"
# opcodes
put_request_opcode: str = "000"
get_request_opcode: str = "001"
change_request_opcode: str = "010"
summary_request_opcode: str = "011"
help_requrest_opcode: str = "100"
class UDPServer: class UDPServer:
def __init__( def __init__(
@ -41,33 +48,45 @@ class UDPServer:
try: try:
while not shut_down: while not shut_down:
message, clientAddress = self.server_socket.recvfrom(2048) message, clientAddress = self.server_socket.recvfrom(2048)
message_in_utf8 = message.decode() request_payload = message.decode()
print( print(
f"myftp> - {self.mode} - received message from client at {clientAddress}: {message_in_utf8}" f"myftp> - {self.mode} - received message from client at {clientAddress}: {request_payload}"
) if self.debug else None ) if self.debug else None
# check for connectivity # check for connectivity
if message_in_utf8 == "ping": if request_payload == "ping":
response_message = "pong" self.server_socket.sendto("pong".encode(), clientAddress)
continue
# list files available on server # list files available on server
elif message_in_utf8 == "list": elif request_payload == "list":
encoded_message = pickle.dumps( encoded_message = pickle.dumps(
get_files_in_directory(self.directory_path) get_files_in_directory(self.directory_path)
) )
self.server_socket.sendto(encoded_message, clientAddress) self.server_socket.sendto(encoded_message, clientAddress)
continue continue
# help request handling
elif request_payload == help_requrest_opcode + "00000":
print(
f"myftp> - {self.mode} - received help request"
) if self.debug else None
rescode = help_rescode
response_data_string = "get,put,summary,change,help,bye"
else: else:
response_message = message_in_utf8.upper() # handle unrecognized request here
pass
payload: bytes = self.build_res_payload(rescode, response_data_string)
self.server_socket.sendto(payload, clientAddress)
print( print(
f"myftp> - {self.mode} - sent message to client at {clientAddress}: {response_message}" f"myftp> - {self.mode} - sent message to client at {clientAddress}: {payload}"
) if self.debug else None ) if self.debug else None
self.server_socket.sendto(response_message.encode(), clientAddress)
except KeyboardInterrupt: except KeyboardInterrupt:
shut_down = True shut_down = True
self.server_socket.close() self.server_socket.close()
@ -76,6 +95,40 @@ class UDPServer:
finally: finally:
print(f"myftp> - {self.mode} - Closed the server socket\n") print(f"myftp> - {self.mode} - Closed the server socket\n")
# assembling the payload to send back to the client
def build_res_payload(self,
rescode: str,
response_data_string: str) -> bytes:
bytes_response_data = response_data_string.encode("utf-8")
data_len = len(bytes_response_data)
print(f"myftp> - {self.mode} - Rescode {rescode}") if self.debug else None
print(f"myftp> - {self.mode} - Length of data {data_len}") if self.debug else None
print(f"myftp> - {self.mode} - Data {response_data_string}") if self.debug else None
# convert to binary
try:
# pad the length of data to make sure it is always 5 bits
# i.e "010" -> "00010"
binary_data_len: str = bin(data_len).zfill(5)
print(f"myftp> - {self.mode} - binary_data_len {binary_data_len[2:]}") if self.debug else None
# create the first byte
# since binary_data_len is of the format 0b00100, we have to remove the first two characters 0b
first_byte: bytes = bytes([int(rescode + binary_data_len[2:], 2)])
print(f"myftp> - {self.mode} - First byte assembled for rescode {rescode}: {bin(int.from_bytes(first_byte, byteorder='big'))[2:]}") if self.debug else None
except Exception as e:
raise Exception(e)
res_payload = first_byte + bytes_response_data
return res_payload
def get_files_in_directory(directory_path: str) -> list[str]: def get_files_in_directory(directory_path: str) -> list[str]:
file_list = [] file_list = []