-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathServer.py
165 lines (151 loc) · 7.84 KB
/
Server.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import socket
import select
from collections import defaultdict, deque
# SERVER
PORT = 8765
SERVER_ADDR = ('localhost', PORT)
MAX_BUFFER_SIZE = 1024
BACKLOG = 5
DELIMITER = "\0"
class Server:
"""
A server that uses select to handle multiple clients at a time.
"""
def __init__(self, port):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allow reuse of local addresses (why is this not the default?)
self.sock.bind(SERVER_ADDR)
self.sock.listen(BACKLOG) # max number of connections waiting to be served
self.MSGS = defaultdict(deque) # complete messages per client
self.buffers = {} # partial messages per client
self.MSGSLEN = {} # message lengths per client [len, sent, recvd]
self.MSGSRECV = {} # messages received from each client
def serv(self, inputs, msgs=None): # inputs = [self.sock]
inputs = inputs
outputs = []
running = True
while running:
readready, writeready, _ = select.select(inputs, outputs, [])
for s in readready:
if s == self.sock:
# handle the server socket
try:
print "Waiting for client to connect"
client, addr = self.sock.accept()
print "Server accepted {}".format(client.getsockname())
# set connection to non-blocking
client.setblocking(False)
# give new connection a queue to store its messages
self.MSGS[client.fileno()].append("")
# give new connection a buffer variable to store characters received
self.buffers[client.fileno()] = ""
# give new connection a key in dict to store # msgs expected
self.MSGSLEN[client.fileno()] = -1
# give new connection a key in dict to store # msgs received
self.MSGSRECV[client.fileno()] = 0
# add connection to list of inputs to monitor
inputs.append(client)
except socket.error as e:
if e.errno == 10054:
print e
continue
else:
# read from an already established client socket
isDone = self.read(s)
if isDone is None:
# s disconnected
print "{} disconnected.".format(s.getsockname())
inputs.remove(s)
# remove its resources
del self.MSGS[s.fileno()]
del self.buffers[s.fileno()]
del self.MSGSLEN[s.fileno()]
del self.MSGSRECV[s.fileno()]
if s in outputs:
# remove s from outputs (no need to respond to it anymore)
outputs.remove(s)
s.close()
elif isDone:
print "Server is done reading from client {}".format(s.fileno())
inputs.remove(s)
if s not in outputs:
outputs.append(s)
else:
# add s to outputs so server can send a response
if s not in outputs:
outputs.append(s)
for s in writeready:
# write to client socket
# print "writing to {}".format(s.getsockname()[1])
isDone = self.write(s)
if isDone:
print "Server is done writing to client {}".format(s.fileno())
if s in outputs:
outputs.remove(s)
#print "closed {}".format(client.getsockname())
s.close()
def write(self, client_sock):
"""write characters"""
dq = self.MSGS[client_sock.fileno()]
if dq:
nextMSG = dq.popleft()
if nextMSG != '':
sent = client_sock.send(nextMSG) # nextMSG is top element of dq (chars up to and including DELIMITER)
print "Server sent {} bytes to {}".format(sent, client_sock.fileno())
if sent == 0:
raise RuntimeError("socket connection broken")
if sent < len(nextMSG):
self.MSGS[client_sock.fileno()].appendleft(nextMSG[sent:]) # add part of msg not sent to front of deque
# check if done writing
# done reading and len(MSGS deque) == 0
if self.MSGSRECV[client_sock.fileno()] == self.MSGSLEN[client_sock.fileno()] + 1:
return len(self.MSGS[client_sock.fileno()]) == 0
else:
# server has not finished receiving all messages
return False
if self.MSGSRECV[client_sock.fileno()] == self.MSGSLEN[client_sock.fileno()] + 1:
return len(self.MSGS[client_sock.fileno()]) == 0
else:
return False
def readBufferMsg(self, client_sock):
""""read characters from buffer"""
buff = self.buffers[client_sock.fileno()]
msgs_recv = self.MSGSRECV[client_sock.fileno()]
next_msg, sep, msgs_rest = buff.partition(DELIMITER)
self.MSGS[client_sock.fileno()].append(next_msg + DELIMITER) # store complete msg on deque
self.MSGSRECV[client_sock.fileno()] = msgs_recv + 1
self.buffers[client_sock.fileno()] = msgs_rest # store partial msg in buffer
#return self.MSGSRECV[client_sock.fileno()] == self.MSGSLEN[client_sock.fileno()] + 1
def read(self, client_sock):
"""
read characters; build messages
"""
buff = self.buffers[client_sock.fileno()]
msgs_recv = self.MSGSRECV[client_sock.fileno()]
chunk = client_sock.recv(MAX_BUFFER_SIZE)
# check that client_sock is still connected
if chunk:
next_msg, sep, msgs_rest = chunk.partition(DELIMITER)
if sep == DELIMITER:
# store number of messages from this client, if not yet stored
if self.MSGSLEN[client_sock.fileno()] < 0:
self.MSGSLEN[client_sock.fileno()] = int(buff + next_msg) # set total number of messages to follow
self.MSGS[client_sock.fileno()].append(buff + next_msg + DELIMITER) # store complete msg
self.buffers[client_sock.fileno()] = msgs_rest
self.MSGSRECV[client_sock.fileno()] = msgs_recv + 1 # first message received
else:
self.MSGS[client_sock.fileno()].append(buff + next_msg + DELIMITER) # store complete msg
self.buffers[client_sock.fileno()] = msgs_rest # store partial msg
self.MSGSRECV[client_sock.fileno()] = msgs_recv + 1 # incr messages received
while DELIMITER in self.buffers[client_sock.fileno()]:
self.readBufferMsg(client_sock)
else:
# delimiter not found
# concatenate partial messages
self.buffers[client_sock.fileno()] = buff + next_msg
return self.MSGSRECV[client_sock.fileno()] == self.MSGSLEN[client_sock.fileno()] + 1
##################################################################################
if __name__ == "__main__":
print 'Echo Server starting'
s = Server(PORT)
s.serv([s.sock])