-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
131 lines (99 loc) · 3.99 KB
/
Copy pathserver.py
File metadata and controls
131 lines (99 loc) · 3.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import socket
import selectors
from socket import socket as Socket
# =============================================================
# SETUP THE LISTENING SOCKET
# =============================================================
server_socket = socket.socket(
family=socket.AF_INET, # this means use IPv4
type=socket.SOCK_STREAM # open up a TCP connection
)
# this tells the server to reuse the port 8080 even after it restarts
server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server_socket.bind(("127.0.0.1", 8080))
server_socket.listen()
print("Listening on 127.0.0.1:8080")
# Sockets are blocking by default.
# By setting this flag, we're telling the kernel to not block incoming connections
# even if the existing ones are still being processed.
# We are relying on epoll to notify us once the existing requests are processed
server_socket.setblocking(False)
# =============================================================
# CREATE AN EPOLL INSTANCE (for windows, it is Selector)
# =============================================================
epoll = selectors.DefaultSelector()
# We register the socket with this epoll instance
# Now the kernel will notify us (the listening socket) once a READ_EVENT is available
epoll.register(
fileobj=server_socket,
events=selectors.EVENT_READ,
data=None
)
# =============================================================
# STATE MANAGER TO MANAGE WHAT NEXT TO BE DONE WITH THE CLIENT CONNECTION (FD)
# =============================================================
# map [file_descriptor (int) : client_socket, handler_function]
handlers = {}
# --------- EVENT HANDLER FUNCTION --------------
def handle_accept():
# once we have accepted the connection, this is when the actual File Descriptor is created
client_socket, addr = server_socket.accept()
print(f"Accepted connection from {addr}")
client_socket.setblocking(False)
# Register the client socket for listening to make accepting connection async
epoll.register(
fileobj=client_socket,
events=selectors.EVENT_READ
)
handlers[client_socket.fileno()] = (client_socket, handle_client_read)
def handle_client_read(client_socket: Socket):
try:
data = client_socket.recv(1024) # read only 1MB of data
if data:
print(f"Received: {data.decode().strip()}")
# now that reading is done, register for writing
epoll.modify(
fileobj=client_socket,
events=selectors.EVENT_WRITE
)
handlers[client_socket.fileno()] = (client_socket, handle_client_write)
else:
# client closed the connection
close_client(client_socket)
except ConnectionResetError:
close_client(client_socket)
def handle_client_write(client_socket: Socket):
client_socket.send(b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello")
close_client(client_socket)
def close_client(client_socket: Socket):
fd = client_socket.fileno()
print(f"Closing connection: fd={fd}")
epoll.unregister(fd)
handlers.pop(fd, None)
try:
client_socket.close()
except OSError:
print("Error closing client connection")
pass
# =============================================================
# MAIN EVENT LOOP
# =============================================================
try:
print("Web server started")
while True:
events = epoll.select(timeout=1) # wait for upto 1s for any event
for key, mask in events:
sock = key.fileobj
fd = key.fd
if sock is server_socket:
handle_accept()
else:
client_socket, handler = handlers.get(fd, (None, None))
handler(client_socket)
except KeyboardInterrupt:
print("Keyboard interruption detected")
finally:
print("Shutting down web server...")
epoll.unregister(server_socket.fileno())
server_socket.close()
print("Web server successfully shutdown")