Skip to content

Commit 3a67ec0

Browse files
committed
Reliable receive
1 parent 73ac80b commit 3a67ec0

File tree

5 files changed

+142
-101
lines changed

5 files changed

+142
-101
lines changed

ProtocolImplementation/ClientProtocolImplementation/ClientProtocol.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import os
2-
from ProtocolImplementation.Protocol import DIMBYTESNUM, BUFFSIZENUM, CODEBYTES, StatusHandler
2+
from ProtocolImplementation.Protocol import DIMBYTESNUM, BUFFSIZENUM, CODEBYTES, StatusHandler, ReliableTransmission
33
import socket
44
from typing import BinaryIO
55

66

77
class ProtocolHandler:
8-
def __init__(self, s: socket):
9-
self.s: socket = s
8+
def __init__(self, s: socket.socket):
9+
self.s: socket.socket = s
1010
self.status_handler = StatusHandler(self.s)
1111

1212
def input(self):
1313
try:
14-
text = self.s.recv(BUFFSIZENUM).decode()
14+
text = ReliableTransmission.recvstring(self.s)
1515
self.status_handler.ok()
1616
self.status_handler.response()
17-
self.s.sendall(input(text)[:BUFFSIZENUM].encode())
17+
input_text = input(text)
18+
input_text += "\n"
19+
self.s.sendall(input().encode())
1820
if not self.status_handler.is_code("OK", self.s.recv(CODEBYTES)):
1921
raise ConnectionError("Server not confirming")
2022
if not self.status_handler.is_code("End", self.s.recv(CODEBYTES)):
@@ -72,8 +74,8 @@ def upload_file(self, file: BinaryIO, size: int): # size [bytes]
7274

7375

7476
class FileHandler:
75-
def __init__(self, s: socket):
76-
self.s: socket = s
77+
def __init__(self, s: socket.socket):
78+
self.s: socket.socket = s
7779
self.protocol_handler = ProtocolHandler(self.s)
7880
self.status_handler = StatusHandler(self.s)
7981

ProtocolImplementation/Protocol.py

+42-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
import socket
33

4-
DIMBYTESNUM = 4
4+
DIMBYTESNUM = 5 # Can be represented up to 2^(5*8) b = 1.0995116 TB
55
BUFFSIZENUM = 1024 # bytes
66
CODEBYTES = 1
77

@@ -20,13 +20,13 @@
2020
"End": b'\x14',
2121
"SendBytes": b'\x15',
2222
"GetBytes": b'\x16',
23-
}
23+
}
2424

2525

2626
class StatusHandler:
2727
Codes = ProtocolCodes
2828

29-
def __init__(self, s: socket):
29+
def __init__(self, s: socket.socket):
3030
self.s = s
3131

3232
def is_code(self, codename, value): # Check if 'value' is equal to the code associated with codename
@@ -70,3 +70,42 @@ def error_file_too_large(self):
7070

7171
def error_file_not_found(self):
7272
self.__send_code("FileNotFound")
73+
74+
75+
class ReliableTransmission:
76+
@classmethod
77+
def recvall(cls, s: socket.socket, size: int):
78+
"""
79+
This method receives size bytes and then returns the received data
80+
81+
:param s: socket over which send data
82+
:param size: number of bytes to receive
83+
:return: received buffer
84+
:rtype: bytes
85+
"""
86+
received_bytes: int = 0
87+
buf: bytes = bytes()
88+
while received_bytes < size:
89+
tmp = s.recv(BUFFSIZENUM)
90+
received_bytes += len(tmp)
91+
buf += received_bytes
92+
return buf
93+
94+
@classmethod
95+
def recvstring(cls, s: socket.socket, eot: str = "\n", encoding: str = "utf-8"):
96+
"""
97+
This method receives until eot occurs and then returns the received data
98+
99+
:param s: socket over which send data
100+
:param eot: End-Of-Transmission character (1 byte-long)
101+
:param encoding: used to decode received bytes
102+
:return: received string (buffer decoded) without eot character
103+
:rtype: str
104+
"""
105+
eot = bytes(eot)
106+
buf: bytes = bytes()
107+
while True:
108+
tmp = s.recv(1)
109+
if eot in tmp: break
110+
buf += tmp
111+
return buf.decode(encoding)

ProtocolImplementation/ServerProtocolImplementation/ServerProtocol.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99

1010
class ProtocolHandler:
11-
def __init__(self, s: socket):
12-
self.s: socket = s
11+
def __init__(self, s: socket.socket):
12+
self.s: socket.socket = s
1313
self.status_handler = StatusHandler(self.s)
1414

1515
def close_connection(self):
@@ -91,7 +91,7 @@ def send_file(self, file, filename):
9191

9292

9393
class FileHandler:
94-
def __init__(self, name, surname, s: socket):
94+
def __init__(self, name, surname, s: socket.socket):
9595
self.name = name
9696
self.surname = surname
9797
self.path = os.path.join(os.getcwd(), f"{name}_{surname}")

client.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66

77
class UserHandler:
8-
def __init__(self, s: socket):
8+
def __init__(self, s: socket.socket):
99
self.s = s
1010

1111
def start(self):

server.py

+87-87
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,87 @@
1-
import socketserver
2-
import logging
3-
from ProtocolImplementation.ServerProtocolImplementation.ServerProtocol import FileHandler, ProtocolHandler
4-
import socket
5-
import os
6-
import argparse
7-
8-
HOST, PORT = "127.0.0.1", 9999
9-
10-
11-
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
12-
def handle(self):
13-
logging.info(f"New connection {self.client_address}")
14-
s: socket = self.request
15-
protocol_handler = ProtocolHandler(s)
16-
17-
name = protocol_handler.get_input("Name: ")
18-
if not name: return
19-
20-
surname = protocol_handler.get_input("Surname: ")
21-
if not surname: return
22-
23-
logging.info(f"Name: {name} Surname: {surname}")
24-
25-
file_handler = FileHandler(name, surname, s)
26-
27-
while True:
28-
cmd = protocol_handler.get_input(">> ") # command
29-
if not cmd: break
30-
logging.debug(f"Received: {cmd}")
31-
32-
if cmd == 'u' or cmd == 'U':
33-
filename = protocol_handler.get_input("Filename: ")
34-
if not filename: break
35-
logging.info(f"Upload request from {self.client_address}. File: {filename}")
36-
file_handler.upload(filename)
37-
elif cmd == 'd' or cmd == 'D':
38-
filename = protocol_handler.get_input("Filename: ")
39-
if not filename: break
40-
logging.info(f"Download request from {self.client_address}. File: {filename}")
41-
file_handler.download(filename)
42-
elif cmd == 'l' or cmd == 'L':
43-
s = "\n"
44-
ls = os.listdir(os.path.join(os.getcwd(), f"{name}_{surname}"))
45-
for ele in ls:
46-
s += f"{ele}\n"
47-
protocol_handler.send_output(s)
48-
elif cmd == 'h' or cmd == 'H':
49-
protocol_handler.send_output("""u/U: upload
50-
d/D: Download
51-
l/L: List
52-
h/H: Help
53-
q: Exit""")
54-
elif cmd == 'q':
55-
protocol_handler.close_connection()
56-
break
57-
else:
58-
protocol_handler.send_output("Command not found")
59-
60-
# s.close_connection()
61-
62-
63-
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
64-
pass
65-
66-
67-
def main():
68-
global HOST, PORT
69-
logging.basicConfig(level=logging.DEBUG, format="%(threadName)s --> %(asctime)s - %(levelname)s: %(message)s",
70-
datefmt="%H:%M:%S")
71-
parser = argparse.ArgumentParser()
72-
parser.add_argument("-a", "--address", help="host address", default=HOST, type=str, dest="address")
73-
parser.add_argument("-p", "--port", help="port number", default=PORT, type=int, dest="port")
74-
args = parser.parse_args()
75-
PORT = args.port
76-
HOST = args.address
77-
logging.info(f"Server running on {HOST}:{PORT}...")
78-
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
79-
try:
80-
server.serve_forever()
81-
except KeyboardInterrupt:
82-
print("\nStopping server. Waiting for the termination of all open connections...")
83-
server.server_close()
84-
85-
86-
if __name__ == "__main__":
87-
main()
1+
import socketserver
2+
import logging
3+
from ProtocolImplementation.ServerProtocolImplementation.ServerProtocol import FileHandler, ProtocolHandler
4+
import socket
5+
import os
6+
import argparse
7+
8+
HOST, PORT = "127.0.0.1", 9999
9+
10+
11+
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
12+
def handle(self):
13+
logging.info(f"New connection {self.client_address}")
14+
s: socket.socket = self.request
15+
protocol_handler = ProtocolHandler(s)
16+
17+
name = protocol_handler.get_input("Name: ")
18+
if not name: return
19+
20+
surname = protocol_handler.get_input("Surname: ")
21+
if not surname: return
22+
23+
logging.info(f"Name: {name} Surname: {surname}")
24+
25+
file_handler = FileHandler(name, surname, s)
26+
27+
while True:
28+
cmd = protocol_handler.get_input(">> ") # command
29+
if not cmd: break
30+
logging.debug(f"Received: {cmd}")
31+
32+
if cmd == 'u' or cmd == 'U':
33+
filename = protocol_handler.get_input("Filename: ")
34+
if not filename: break
35+
logging.info(f"Upload request from {self.client_address}. File: {filename}")
36+
file_handler.upload(filename)
37+
elif cmd == 'd' or cmd == 'D':
38+
filename = protocol_handler.get_input("Filename: ")
39+
if not filename: break
40+
logging.info(f"Download request from {self.client_address}. File: {filename}")
41+
file_handler.download(filename)
42+
elif cmd == 'l' or cmd == 'L':
43+
s = "\n"
44+
ls = os.listdir(os.path.join(os.getcwd(), f"{name}_{surname}"))
45+
for ele in ls:
46+
s += f"{ele}\n"
47+
protocol_handler.send_output(s)
48+
elif cmd == 'h' or cmd == 'H':
49+
protocol_handler.send_output("""u/U: upload
50+
d/D: Download
51+
l/L: List
52+
h/H: Help
53+
q: Exit""")
54+
elif cmd == 'q':
55+
protocol_handler.close_connection()
56+
break
57+
else:
58+
protocol_handler.send_output("Command not found")
59+
60+
# s.close_connection()
61+
62+
63+
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
64+
pass
65+
66+
67+
def main():
68+
global HOST, PORT
69+
logging.basicConfig(level=logging.DEBUG, format="%(threadName)s --> %(asctime)s - %(levelname)s: %(message)s",
70+
datefmt="%H:%M:%S")
71+
parser = argparse.ArgumentParser()
72+
parser.add_argument("-a", "--address", help="host address", default=HOST, type=str, dest="address")
73+
parser.add_argument("-p", "--port", help="port number", default=PORT, type=int, dest="port")
74+
args = parser.parse_args()
75+
PORT = args.port
76+
HOST = args.address
77+
logging.info(f"Server running on {HOST}:{PORT}...")
78+
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
79+
try:
80+
server.serve_forever()
81+
except KeyboardInterrupt:
82+
print("\nStopping server. Waiting for the termination of all open connections...")
83+
server.server_close()
84+
85+
86+
if __name__ == "__main__":
87+
main()

0 commit comments

Comments
 (0)