MyFTP/src/myftp/client.py

193 lines
6.2 KiB
Python

from socket import socket, AF_INET, SOCK_DGRAM
from typing import Tuple
from argparse import ArgumentParser
import os
import pickle
# custome type to represent the hostname(server name) and the server port
Address = Tuple[str, int]
class UDPClient:
def __init__(self, server_name: str, server_port: int, debug: bool):
self.server_name: str = server_name
self.server_port: int = server_port
self.mode: str = "UDP"
self.pong_received: bool = False
self.debug = debug
def run(self):
# server cannot be reached, stop the client immediately
if not self.pong_received:
return
client_socket = None # shutup the variableNotBound warning
while True:
try:
client_socket = socket(AF_INET, SOCK_DGRAM)
client_socket.connect((self.server_name, self.server_port))
# get command from user
while (command := input(f"myftp> - {self.mode} - : ")) not in [
"put",
"get",
"summary",
"change",
"help",
"list",
"bye",
]:
print(
f"myftp> - {self.mode} - : Invalid command. Supported commands are put, get, summary, change, list and help"
)
# handling the "bye" command
if command == "bye":
client_socket.close()
print(f"myftp> - {self.mode} - Session is terminated")
break
elif command == "list":
client_socket.send(command.encode())
encoded_message, server_address = client_socket.recvfrom(4096)
file_list = pickle.loads(encoded_message)
print(f"Received file list from {server_address}: {file_list}")
client_socket.close()
continue
client_socket.send(command.encode())
modified_message = client_socket.recv(2048)
print(modified_message.decode())
except ConnectionRefusedError:
print(
f"myftp> - {self.mode} - ConnectionRefusedError happened. Please restart the client program, make sure the server is running and/or put a different server name and server port."
)
finally:
client_socket.close()
# ping pong UDP
def check_udp_server(self):
# Create a UDP socket
client_socket = socket(AF_INET, SOCK_DGRAM)
# will time out after 5 seconds
client_socket.settimeout(5)
try:
# Send a test message to the server
message = b"ping"
client_socket.sendto(message, (self.server_name, self.server_port))
# Receive the response
data, _ = client_socket.recvfrom(1024)
# If the server responds, consider the address valid
print(
f"myftp> - {self.mode} - Server at {self.server_name}:{self.server_port} is valid. Response received: {data.decode('utf-8')}"
)
# code reached here meaning no problem with the connection
self.pong_received = True
except TimeoutError:
# Server did not respond within the specified timeout
print(
f"myftp> - {self.mode} - Server at {self.server_name} did not respond within 5 seconds. Check the address or server status."
)
finally:
# Close the socket
client_socket.close()
def get_address_input() -> Address:
while True:
try:
# Get input as a space-separated string
input_string = input("myftp>Provide IP address and Port number\n")
# Split the input into parts
input_parts = input_string.split()
# Ensure there are exactly two parts
if len(input_parts) != 2:
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
string_part, int_part = input_parts
address = (string_part, int(int_part))
# Valid tuple, return it
return address
except ValueError as e:
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."
)
def check_directory(path):
if os.path.exists(path):
if os.path.isdir(path):
if os.access(path, os.R_OK) and os.access(path, os.W_OK):
return True
else:
print(f"Error: The directory '{path}' is not readable or writable.")
else:
print(f"Error: '{path}' is not a directory.")
else:
print(f"Error: The directory '{path}' does not exist.")
return False
def init():
arg_parser = ArgumentParser(description="A FTP client written in Python")
arg_parser.add_argument(
"--debug",
type=int,
choices=[0, 1],
default=0,
required=False,
help="Enable or disable the flag (0 or 1)",
)
arg_parser.add_argument(
"--directory", required=True, type=str, help="Path to the client directory"
)
args = arg_parser.parse_args()
while (
protocol_selection := input("myftp>Press 1 for TCP, Press 2 for UDP\n")
) not in {"1", "2"}:
print("myftp>Invalid choice. Press 1 for TCP, Press 2 for UDP")
if not check_directory(args.directory):
print(
f"The directory '{args.directory}' does not exists or is not readable/writable."
)
return
# UDP client selected here
if protocol_selection == "2":
user_supplied_address = get_address_input()
udp_client = UDPClient(
user_supplied_address[0], user_supplied_address[1], args.debug
)
udp_client.check_udp_server()
udp_client.run()
else:
# tcp client here
pass
if __name__ == "__main__":
init()