Skip to content

Commit 27e8c75

Browse files
author
Francesco Mecatti
committed
Merge remote-tracking branch 'origin/master'
2 parents aa0a3c9 + 85ba4c0 commit 27e8c75

File tree

9 files changed

+253
-104
lines changed

9 files changed

+253
-104
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import argparse
2+
import logging
3+
import threading
4+
import asyncio
5+
import sys
6+
7+
"""
8+
Connect to this server using netcat or similar utilities
9+
"""
10+
11+
HOST, PORT = "127.0.0.1", 9999
12+
13+
# TODO: mantieni numero di client connessi e ritorna lista con indirizzi
14+
# TODO: trasforma write in writelines e termina con \n
15+
# TODO: usa readline e termina comando con \n
16+
17+
18+
def handle_command(cmd: str, client_stream: asyncio.StreamWriter):
19+
if not cmd: return
20+
if cmd == '0':
21+
return f"Client address: {client_stream.get_extra_info('peername')}\n"
22+
elif cmd == '1':
23+
return f"Socket address: {client_stream.get_extra_info('sockname')}\n"
24+
elif cmd == '2':
25+
return f"Current thread name: {threading.current_thread().getName()}\n"
26+
elif cmd == '3':
27+
return f"Alive threads number: {threading.active_count()}\n"
28+
elif cmd == '4':
29+
return f"Alive threads: {[t.getName() for t in threading.enumerate()]}\n"
30+
else: # Help
31+
return """'0': Return the remote address (client) to which the socket is connected;
32+
'1': Return the server socket's own address;
33+
'2': Return the current thread name;
34+
'3': Return the number of alive threads;
35+
'4': Return the list of names of alive threads (comma separated);
36+
'q': Quit server when all the clients will be disconnected;
37+
"""
38+
39+
40+
async def server_handler(reader, writer):
41+
addr = writer.get_extra_info('peername')
42+
logging.info(f"Serving {addr}")
43+
while True:
44+
writer.write(">> ".encode())
45+
await writer.drain()
46+
try:
47+
data = await reader.read(1024)
48+
except ConnectionResetError:
49+
logging.error(f"Closed connection - Reset Connection Error: {addr}")
50+
break
51+
cmd = data.decode().strip()
52+
53+
logging.info(f"Received {cmd} from {addr}")
54+
55+
if not cmd:
56+
writer.close()
57+
break
58+
elif cmd == 'q':
59+
logging.warning(f"Closed connection: {addr}")
60+
writer.close()
61+
break
62+
else:
63+
writer.write(handle_command(cmd, writer).encode()) # TODO: writeline
64+
await writer.drain()
65+
66+
67+
async 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() # TODO: non funzionante con asyncio
75+
PORT = args.port
76+
HOST = args.address
77+
78+
logging.info(f"Server running on {HOST}:{PORT}...")
79+
80+
server = await asyncio.start_server(server_handler, HOST, PORT)
81+
82+
async with server:
83+
await server.serve_forever()
84+
85+
86+
if __name__ == "__main__":
87+
try:
88+
asyncio.run(main())
89+
except KeyboardInterrupt:
90+
logging.warning("Server stopping...")

OtherSocketExamples/multithreaded_simple_server.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"""
77
Connect to this server using netcat or similar utilities
88
"""
9+
# TODO: termina con \n --> adatta ricezione e trasmissione
910

1011
# '0': Return the remote address (client) to which the socket is connected;
1112
# '1': Return the server socket's own address;
@@ -19,10 +20,14 @@
1920

2021

2122
def client_handle(conn, addr):
22-
logging.info(f"New connection {addr}".encode())
23+
logging.info(f"New connection {addr}")
2324
while True:
2425
conn.sendall(">> ".encode())
25-
data = conn.recv(1024).decode() # command
26+
try:
27+
data = conn.recv(1024).decode() # command
28+
except ConnectionResetError:
29+
logging.error(f"Closed connection - Reset Connection Error: {addr}")
30+
break
2631
cmd = data[0]
2732
logging.debug(f"Received: {cmd}")
2833
if not cmd: break

OtherSocketExamples/select_simple_server.py

+6
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
from select import select
55
from collections import defaultdict
66
import threading
7+
import sys
78

89
"""
910
Connect to this server using netcat or similar utilities
1011
"""
1112

1213
HOST, PORT = "127.0.0.1", 9999
1314

15+
# TODO: mantieni numero e lista client connessi
16+
# TODO: sistema socket asincrono con sendall --> fai send e togli quello che ha inviato
17+
# TODO: termina con \n --> adatta ricezione e trasmissione
18+
1419

1520
def handle_command(cmd: str, server_sock: socket, client_sock: socket):
1621
if not cmd: return
@@ -107,6 +112,7 @@ def main():
107112
logging.warning("Server stopping...")
108113
finally:
109114
server_sock.close()
115+
sys.exit()
110116

111117

112118
if __name__ == "__main__":

OtherSocketExamples/socketserver_simple_server.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,19 @@
99

1010
HOST, PORT = "127.0.0.1", 9999
1111

12+
# TODO: termina con \n --> adatta ricezione e trasmissione
1213

1314
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
1415

1516
def handle(self):
1617
logging.info(f"New connection {self.client_address}".encode())
1718
while True:
1819
self.request.sendall(">> ".encode())
19-
data = self.request.recv(1024).decode() # command
20+
try:
21+
data = self.request.recv(1024).decode() # command
22+
except ConnectionResetError:
23+
logging.error(f"Closed connection - Reset Connection Error: {self.client_address}")
24+
break
2025
cmd = data[0]
2126
logging.debug(f"Received: {cmd}")
2227
if not cmd: break

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

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

4-
DIMBYTESNUM = 4
4+
# TODO: sanifica input utente in server --> togli . e / (tutti i caratteri che rendono possibile la navigazione tra cartelle) --> potrebbe rimanare stringa vuota
5+
6+
DIMBYTESNUM = 5 # Can be represented up to 2^(5*8) b = 1 TB
57
BUFFSIZENUM = 1024 # bytes
68
CODEBYTES = 1
79

@@ -20,13 +22,13 @@
2022
"End": b'\x14',
2123
"SendBytes": b'\x15',
2224
"GetBytes": b'\x16',
23-
}
25+
}
2426

2527

2628
class StatusHandler:
2729
Codes = ProtocolCodes
2830

29-
def __init__(self, s: socket):
31+
def __init__(self, s: socket.socket):
3032
self.s = s
3133

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

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

0 commit comments

Comments
 (0)