diff --git a/README.md b/README.md index accb79b..1cb5e35 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,15 @@ Zero. Only python standard libs were used. Tested on Python 3.11 You can run `python3 src/myftp/client.py --directory ` to start the client or `python3 src/myftp/client.py --debug 1 --directory ` for debugging purposes. +Some example test commands: + +- `get file_server.txt` +- `summary numbers.txt` +- `put file_local.txt` +- `put image_local.png` +- `change file_server.txt file_server1.txt` +- `help` + ### Server By default, the server IP address or hostname or server name will be `0.0.0.0` or `localhost` (meaning it will bind to all interfaces). The `--port_number` flag, if not specified will be by default `12000`. diff --git a/src/myftp/client.py b/src/myftp/client.py index 75af8a3..ab017a4 100644 --- a/src/myftp/client.py +++ b/src/myftp/client.py @@ -3,7 +3,7 @@ # Description: FTP client (both UDP and TCP implemented) -from socket import socket, AF_INET, SOCK_DGRAM +from socket import socket, AF_INET, SOCK_DGRAM, SOCK_STREAM from typing import Pattern, Tuple, Optional from argparse import ArgumentParser import traceback @@ -58,8 +58,15 @@ class Client: self.debug = debug def run(self): - client_socket = socket(AF_INET, SOCK_DGRAM) - client_socket.settimeout(10) + self.client_socket = socket( + AF_INET, (SOCK_DGRAM if self.protocol == "UDP" else SOCK_STREAM) + ) + self.client_socket.settimeout(10) + + # only if using TCP + self.client_socket.connect( + (self.server_name, self.server_port) + ) if self.protocol == "TCP" else None try: while True: @@ -68,7 +75,7 @@ class Client: # handling the "bye" command if command == "bye": - client_socket.close() + self.client_socket.close() print(f"myftp> - {self.protocol} - Session is terminated") break @@ -160,9 +167,12 @@ class Client: f"myftp> - {self.protocol} - sent payload {bin(int.from_bytes(payload, byteorder='big'))[2:]} to the server" # type: ignore ) if self.debug else None - client_socket.sendto(payload, (self.server_name, self.server_port)) # type: ignore + if self.protocol == "UDP": + self.client_socket.sendto(payload, (self.server_name, self.server_port)) # type: ignore + else: + self.client_socket.sendall(payload) # type: ignore - response_payload = client_socket.recv(2048) + response_payload = self.client_socket.recv(2048) self.parse_response_payload(response_payload) @@ -185,7 +195,7 @@ class Client: print(traceback_info) finally: - client_socket.close() + self.client_socket.close() def parse_response_payload(self, response_payload: bytes): first_byte = bytes([response_payload[0]]) diff --git a/src/myftp/server.py b/src/myftp/server.py index 0ac9b2c..07c1ef6 100644 --- a/src/myftp/server.py +++ b/src/myftp/server.py @@ -3,7 +3,7 @@ # Description: FTP server (both UDP and TCP implemented) -from socket import socket, AF_INET, SOCK_DGRAM +from socket import socket, AF_INET, SOCK_DGRAM, SOCK_STREAM from argparse import ArgumentParser from typing import Optional, Tuple import traceback @@ -50,8 +50,14 @@ class Server: self.debug = debug def run(self): - self.server_socket = socket(AF_INET, SOCK_DGRAM) - self.server_socket.bind((self.server_name, self.server_port)) + server_socket = socket( + AF_INET, (SOCK_DGRAM if self.protocol == "UDP" else SOCK_STREAM) + ) + + server_socket.bind((self.server_name, self.server_port)) + + # only needed for TCP + server_socket.listen(5) if self.protocol == "TCP" else None print( f"myftp> - {self.protocol} - Server is ready to receive at {self.server_name}:{self.server_port}" @@ -60,12 +66,27 @@ class Server: shut_down = False try: + if self.protocol == "TCP": + client_socket, clientAddress = server_socket.accept() + + print( + f"myftp> - {self.protocol} Connected to TCP client at {clientAddress}" + ) if self.debug else None + while not shut_down: print( f"myftp> - {self.protocol} ------------------------------------------------------------------" ) if self.debug else None - req_payload, clientAddress = self.server_socket.recvfrom(2048) + if self.protocol == "UDP": + req_payload, clientAddress = server_socket.recvfrom(2048) + else: + req_payload = client_socket.recv(2048) # type: ignore + + # TCP client disconnected + if not req_payload: + client_socket.close() # type: ignore + return first_byte = bytes([req_payload[0]]) @@ -74,7 +95,7 @@ class Server: ) print( - f"myftp> - {self.protocol} - Received message from client at {clientAddress}: {req_payload}" + f"myftp> - {self.protocol} - Received message from client at {clientAddress}: {req_payload}" # type: ignore ) if self.debug else None # help request handling @@ -159,15 +180,22 @@ class Server: response_data=response_data, # type:ignore ) - self.server_socket.sendto(res_payload, clientAddress) + if self.protocol == "UDP": + server_socket.sendto(res_payload, clientAddress) # type: ignore + else: + client_socket.sendall(res_payload) # type: ignore print( - f"myftp> - {self.protocol} - Sent message to client at {clientAddress}: {res_payload}" + f"myftp> - {self.protocol} - Sent message to client at {clientAddress}: {res_payload}" # type: ignore ) if self.debug else None except KeyboardInterrupt: shut_down = True - self.server_socket.close() + if self.protocol == "UDP": + server_socket.close() + else: + client_socket.close() # type: ignore + print(f"myftp> - {self.protocol} - Server shutting down") finally: