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 ()
0 commit comments