-
Notifications
You must be signed in to change notification settings - Fork 0
/
server_1.6.py
123 lines (106 loc) · 4.19 KB
/
server_1.6.py
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
"""server_1.6.py
Let's go async/await!
"""
# PEP8 eat your heart out
from socket import *
from fib import fib
from collections import deque
# way of watching sockets for read and write signals... (sort of an os
# level polling of the registered sockets)
from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
from concurrent.futures import ProcessPoolExecutor as Pool
from concurrent.futures import as_completed
pool = Pool(4)
class Loop:
def __init__(self):
self.ready = deque()
self.selector = DefaultSelector()
self.futures = {}
def create_task(self, task):
self.ready.append(task)
def run_forever(self):
while True:
while not self.ready:
completed_futures = [future for future in self.futures if not future.running()]
for future in completed_futures:
self.ready.append(self.futures.pop(future))
# so select() is blocking. If set a negative time out it won't block.
events = self.selector.select(-1)
# add these socket events and unregister them from listened to:
for key, _ in events:
self.ready.append(key.data) # add the task to the ready queue
self.selector.unregister(key.fileobj)
while self.ready:
self.current_task = self.ready.popleft()
# try to run current_task...
try:
# run task to next yield point
reason, what = self.current_task.send(None)
# reason, what = self.current_task.send(None)
if reason == 'waiting_to_accept':
self.selector.register(what, EVENT_READ, self.current_task)
elif reason == 'waiting_to_read':
self.selector.register(what, EVENT_READ, self.current_task)
elif reason == 'waiting_to_write':
self.selector.register(what, EVENT_WRITE, self.current_task)
elif reason == 'waiting_for_future':
self.futures[what] = self.current_task
else:
raise RuntimeError(
'Something bad happened... er. reason={}'.format(reason))
except StopIteration:
pass
async def sock_recv(self, sock, maxbytes):
# wait to read from the socket
await read_wait(sock)
return sock.recv(maxbytes)
async def sock_accept(self, sock):
# wait to read/hear from the socket
await accept_wait(sock)
return sock.accept()
async def sock_sendall(self, sock, data):
while data:
# wait to be able to write to the socket
await write_wait(sock)
nsent = sock.send(data)
data = data[nsent:]
def run_in_executor(self, executor, func, *args):
return executor.submit(func, *args)
from types import coroutine
@coroutine
def write_wait(sock):
yield 'waiting_to_write', sock
@coroutine
def accept_wait(sock):
yield 'waiting_to_accept', sock
@coroutine
def read_wait(sock):
yield 'waiting_to_read', sock
@coroutine
def future_wait(future):
yield 'waiting_for_future', future
return future.result()
loop = Loop()
async def fib_server(address):
sock = socket(AF_INET, SOCK_STREAM) # TCP socket
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sock.bind(address)
sock.listen(5)
print("Running fib_server. Yay!")
while True:
client, addr = await loop.sock_accept(sock) # BLOCKING I/O
print("Connection to client at address: ", addr)
loop.create_task(fib_handler(client))
async def fib_handler(client):
while True:
req = await loop.sock_recv(client, 100) # BLOCKING I/O
if not req:
break
n = int(req)
future = loop.run_in_executor(pool, fib, n)
result = await future_wait(future)
resp = str(result).encode('utf-8') + b'\n'
await loop.sock_sendall(client, resp) # BLOCKING I/O
print("Closed connection to client")
loop.create_task(fib_server(("", 25000)))
loop.run_forever() # run the server