Skip to content

Commit 95e7e5f

Browse files
New examples
1 parent aa89c0f commit 95e7e5f

8 files changed

+281
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from socket import socket, AF_INET, SOCK_STREAM
2+
import threading
3+
import logging
4+
import argparse
5+
6+
"""
7+
Connect to this server using netcat or similar utilities
8+
"""
9+
10+
# '0': Return the remote address (client) to which the socket is connected;
11+
# '1': Return the server socket's own address;
12+
# '2': Return the current thread name;
13+
# '3': Return the number of alive threads;
14+
# '4': Return the list of names of alive threads (comma separated);
15+
# 'q': Quit server when all the clients will be disconnected;
16+
17+
HOST, PORT = "127.0.0.1", 9999
18+
s = socket(AF_INET, SOCK_STREAM)
19+
20+
def client_handle(conn, addr):
21+
logging.info(f"New connection {addr}".encode())
22+
while True:
23+
conn.sendall(">> ".encode())
24+
data = conn.recv(1024).decode() # command
25+
cmd = data[0]
26+
logging.debug(f"Received: {cmd}")
27+
if not cmd: break
28+
if cmd == '0':
29+
conn.sendall(f"Client address: {addr}\n".encode())
30+
elif cmd == '1':
31+
conn.sendall(f"Server address: {s.getsockname()}\n".encode())
32+
elif cmd == '2':
33+
conn.sendall(f"Current thread name: {threading.current_thread().getName()}\n".encode())
34+
elif cmd == '3':
35+
conn.sendall(f"Alive threads number: {threading.active_count()}\n".encode())
36+
elif cmd == '4':
37+
conn.sendall(f"Alive threads: {[t.getName() for t in threading.enumerate()]}\n".encode())
38+
elif cmd == 'q':
39+
break
40+
else: # Help
41+
conn.sendall("""'0': Return the remote address (client) to which the socket is connected;
42+
'1': Return the server socket’s own address;
43+
'2': Return the current thread name;
44+
'3': Return the number of alive threads;
45+
'4': Return the list of names of alive threads (comma separated);
46+
'q': Quit server when all the clients will be disconnected;
47+
""".encode())
48+
49+
conn.close()
50+
logging.info(f"Connection closed by client {addr}")
51+
52+
53+
def server_loop(s):
54+
client_id = 1
55+
while True:
56+
logging.info("Waiting for new connection")
57+
conn, addr = s.accept()
58+
handle_thread = threading.Thread(target=client_handle, args=(conn, addr), daemon=True)
59+
handle_thread.setName(f"client_{client_id}")
60+
handle_thread.start()
61+
client_id += 1
62+
63+
def main():
64+
global HOST, PORT
65+
logging.basicConfig(level=logging.DEBUG, format="%(threadName)s --> %(asctime)s - %(levelname)s: %(message)s", datefmt="%H:%M:%S")
66+
parser = argparse.ArgumentParser()
67+
parser.add_argument("-a", "--address", help="host address", default=HOST, type=str, dest="address")
68+
parser.add_argument("-p", "--port", help="port number", default=PORT, type=int, dest="port")
69+
args = parser.parse_args()
70+
PORT = args.port
71+
HOST = args.address
72+
s.bind((HOST, PORT))
73+
s.listen(5)
74+
logging.info(f"Server running on {HOST}:{PORT}...")
75+
server = threading.Thread(target=server_loop, args=(s, ), daemon=True)
76+
server.setName("server")
77+
server.start()
78+
try:
79+
while True:
80+
pass
81+
except KeyboardInterrupt:
82+
logging.warning("Server stopping...")
83+
finally:
84+
s.close()
85+
86+
87+
if __name__ == "__main__":
88+
main()
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import socket
2+
import argparse
3+
import logging
4+
from select import select
5+
from collections import defaultdict
6+
import threading
7+
8+
HOST, PORT = "127.0.0.1", 9999
9+
10+
def handle_command(cmd: str, sock: socket):
11+
# sock.sendall(">> ".encode())
12+
# data = sock.recv(1024).decode() # command
13+
# cmd = data[0]
14+
logging.debug(f"Received: {cmd}")
15+
if not cmd: return
16+
if cmd == '0': # TODO
17+
return f"Client address: {sock.getpeername()}\n"
18+
elif cmd == '1':
19+
return f"Socket address: {sock.getsockname()}\n"
20+
elif cmd == '2':
21+
return f"Current thread name: {threading.current_thread().getName()}\n"
22+
elif cmd == '3':
23+
return f"Alive threads number: {threading.active_count()}\n"
24+
elif cmd == '4':
25+
return f"Alive threads: {[t.getName() for t in threading.enumerate()]}\n"
26+
elif cmd == 'q': # TODO
27+
return
28+
else: # Help # TODO
29+
return """'0': Return the remote address (client) to which the socket is connected;
30+
'1': Return the server socket's own address;
31+
'2': Return the current thread name;
32+
'3': Return the number of alive threads;
33+
'4': Return the list of names of alive threads (comma separated);
34+
'q': Quit server when all the clients will be disconnected;
35+
"""
36+
37+
def server_loop(server_sock: socket):
38+
sock_io = list()
39+
data = defaultdict(bytes)
40+
waiting = defaultdict(bool) # True --> waiting data from the client
41+
sock_io.append(server_sock)
42+
while True:
43+
# logging.info("Waiting for events")
44+
readable, writable, exceptional = select(sock_io, sock_io, [])
45+
for r in readable:
46+
if r is server_sock:
47+
conn, addr = r.accept()
48+
sock_io.append(conn)
49+
logging.info(f"New connection: {addr}")
50+
else:
51+
buf = bytes()
52+
try:
53+
while True:
54+
buf += r.recv(1024)
55+
except socket.error:
56+
pass
57+
58+
if not buf:
59+
logging.warning(f"Closed connection: {r.getsockname()}")
60+
r.close()
61+
sock_io.remove(r)
62+
else:
63+
data[r] = buf # Saving data
64+
waiting[r] = False
65+
logging.info(f"Received {buf.decode().strip()} from {r.getsockname()}")
66+
67+
for w in writable:
68+
if data[w]:
69+
logging.info(f"Consuming {data[w]}")
70+
w.sendall(handle_command(data[w].decode().strip(), server_sock).encode())
71+
del data[w]
72+
elif not waiting[w]:
73+
w.sendall(">> ".encode())
74+
logging.info(f"Requested command from {r.getsockname()}")
75+
waiting[w] = True
76+
77+
def main():
78+
global HOST, PORT
79+
logging.basicConfig(level=logging.DEBUG, format="%(threadName)s --> %(asctime)s - %(levelname)s: %(message)s",
80+
datefmt="%H:%M:%S")
81+
parser = argparse.ArgumentParser()
82+
parser.add_argument("-a", "--address", help="host address", default=HOST, type=str, dest="address")
83+
parser.add_argument("-p", "--port", help="port number", default=PORT, type=int, dest="port")
84+
args = parser.parse_args()
85+
PORT = args.port
86+
HOST = args.address
87+
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
88+
server_sock.setblocking(False)
89+
server_sock.bind((HOST, PORT))
90+
server_sock.listen(5)
91+
logging.info(f"Server running on {HOST}:{PORT}...")
92+
server = threading.Thread(target=server_loop, args=(server_sock,), daemon=True)
93+
server.setName("server")
94+
server.start()
95+
try:
96+
while True:
97+
pass
98+
except KeyboardInterrupt:
99+
logging.warning("Server stopping...")
100+
finally:
101+
server_sock.close()
102+
103+
104+
if __name__ == "__main__":
105+
main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import socketserver
2+
import threading
3+
import logging
4+
import argparse
5+
6+
HOST, PORT = "127.0.0.1", 9999
7+
8+
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
9+
10+
def handle(self):
11+
logging.info(f"New connection {self.client_address}".encode())
12+
while True:
13+
self.request.sendall(">> ".encode())
14+
data = self.request.recv(1024).decode() # command
15+
cmd = data[0]
16+
logging.debug(f"Received: {cmd}")
17+
if not cmd: break
18+
if cmd == '0':
19+
self.request.sendall(f"Client address: {self.client_address}\n".encode())
20+
elif cmd == '1':
21+
self.request.sendall(f"Socket address: {self.server.server_address}\n".encode())
22+
elif cmd == '2':
23+
self.request.sendall(f"Current thread name: {threading.current_thread().getName()}\n".encode())
24+
elif cmd == '3':
25+
self.request.sendall(f"Alive threads number: {threading.active_count()}\n".encode())
26+
elif cmd == '4':
27+
self.request.sendall(f"Alive threads: {[t.getName() for t in threading.enumerate()]}\n".encode())
28+
elif cmd == 'q':
29+
break
30+
else: # Help
31+
self.request.sendall("""'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+
""".encode())
38+
39+
self.request.close()
40+
logging.info(f"Connection closed by client {self.client_address}")
41+
42+
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
43+
pass
44+
45+
def main():
46+
global HOST, PORT
47+
logging.basicConfig(level=logging.DEBUG, format="%(threadName)s --> %(asctime)s - %(levelname)s: %(message)s", datefmt="%H:%M:%S")
48+
parser = argparse.ArgumentParser()
49+
parser.add_argument("-a", "--address", help="host address", default=HOST, type=str, dest="address")
50+
parser.add_argument("-p", "--port", help="port number", default=PORT, type=int, dest="port")
51+
args = parser.parse_args()
52+
PORT = args.port
53+
HOST = args.address
54+
logging.info(f"Server running on {HOST}:{PORT}...")
55+
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
56+
try:
57+
server.serve_forever()
58+
except KeyboardInterrupt:
59+
logging.warning("Stopping...")
60+
finally:
61+
server.server_close()
62+
63+
64+
if __name__ == "__main__":
65+
main()
File renamed without changes.
File renamed without changes.

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ run `py client.py`
2626

2727
## Protocol diagrams
2828

29-
![Protocol Input Output](Protocol_Input_Output.png)
29+
![ProtocolInputOutput](ProtocolInputOutput.png)
3030

31-
![Protocol Upload Download](Protocol_Upload_Download.png)
31+
![ProtocolUploadDownload](ProtocolUploadDownload.png)
3232

3333

3434
## Authors

client.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import socket
22
from ProtocolImplementation.ClientProtocolImplementation.ClientProtocol import CODEBYTES, FileHandler, ProtocolHandler, StatusHandler
3+
import argparse
34

45
class UserHandler:
56
def __init__(self, s: socket):
@@ -21,8 +22,17 @@ def start(self):
2122
file_handler.download()
2223

2324
def main():
25+
parser = argparse.ArgumentParser()
26+
parser.add_argument("-a", "--address", help="host address", default=None, type=str, dest="address")
27+
parser.add_argument("-p", "--port", help="port number", default=None, type=int, dest="port")
28+
args = parser.parse_args()
29+
ip = args.address
30+
port = args.port
2431
try:
25-
ip, port = input("Ip address: "), input("Port: ")
32+
if not ip:
33+
ip = input("Ip address: ")
34+
if not port:
35+
port = input("Port: ")
2636
except KeyboardInterrupt:
2737
print("\n\nExiting...")
2838
exit()

server.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
from ProtocolImplementation.ServerProtocolImplementation.ServerProtocol import FileHandler, ProtocolHandler
44
import socket
55
import os
6+
import argparse
67

7-
HOST, PORT = "", 1234
8+
HOST, PORT = "127.0.0.1", 9999
89

910

1011
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
@@ -64,9 +65,16 @@ class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
6465

6566

6667
def main():
68+
global HOST, PORT
6769
logging.basicConfig(level=logging.DEBUG, format="%(threadName)s --> %(asctime)s - %(levelname)s: %(message)s",
6870
datefmt="%H:%M:%S")
69-
logging.info("Server running...")
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}...")
7078
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
7179
try:
7280
server.serve_forever()

0 commit comments

Comments
 (0)