-
Notifications
You must be signed in to change notification settings - Fork 0
/
serverLinux.py
321 lines (294 loc) · 10.6 KB
/
serverLinux.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
from curses import delay_output
from socket import *
from os import listdir
from os.path import isfile, join
from frUDP import *
serverSocket = socket(AF_INET, SOCK_STREAM)
host = gethostbyname(gethostname())
port = 55000
msgs = [] # [list of msgs]
pms = {} # name, [list of msgs]
clients = {} # id, its name (online clients only)
try:
serverSocket.bind((host, port))
except:
print("error")
exit()
print('Socket is listening at ', host)
serverSocket.listen(5)
def listen_for_acks(values, nacks, udpSocket):
"""
listens for ack\nack\ack-all messages sent by the client while the file transfers
:param values:
:param nacks:
:param udpSocket:
:return:
"""
while len(nacks) != 0 or not values[0]:
if not values[0]:
try:
data, addr = udpSocket.recvfrom(65535)
except:
break
checksum, _, data = unwrap(data)
if checksum == 1:
print("what to do when ack msg gets wrong checksum?")
if data[:4] == "NACK":
print("got nack ")
if values[1]>8:
values[1]-=3 # CC values are imprecise
if int(data[4:7]) not in nacks:
nacks.append(int(data[4:7]))
if data == "ACK-ALL":
values[0] = True
elif data[:3] == "ACK":
list = eval(data[3:])
if values[1]<60: # CC values are imprecise
values[1]+=1 # CC values are imprecise
flag = True
for i in list:
if i in nacks:
if nacks.index(i) > 2 and values[1]>8: # CC values are imprecise
# this checks for latency, where a later packet arrived before it's predecessor
if flag:
flag = False
values[1]-=1.2 # CC values are imprecise
nacks.remove(i)
values[0] = True
def frUDP(connection, file):
"""
frUDP connection for controlled file transfer
:param connection:
:param file:
:return:
"""
connection.sendall(str.encode("connect_frudp"))
udpSocket = socket(AF_INET, SOCK_DGRAM)
udport = 55001
ready = False
while not ready:
try:
udpSocket.bind((host, udport))
ready = True
except:
print("could not udp at "+str(udport))
udport+=1
if udport>55015:
break
if not ready:
connection.sendall(str.encode("e"))
exit_thread()
else:
# send over TCP the dedicated port for file transfer
connection.sendall(str.encode(str(udport)+file))
addr = serverside_threewayhandshake(udpSocket, connection, file)
segments = []
nacks = []
# save file to memory
f = open("files/"+file, "rb")
b=0
id = 0
filesize = os.path.getsize("files/" + file)
while b<filesize+1000:
segments.append(f.read(1000))
nacks.append(id)
id+=1
b+=1000
segments.append(f.read(1000))
nacks.append(id)
values = []
values.append(False)
values.append(60)
values[1] = 10 # the amount of segments sent every time
# start sending the file
start_new_thread(listen_for_acks, (values, nacks, udpSocket))
while not values[0]:
data = {}
for id in range(len(segments)):
if id in nacks and len(data)<=values[1]:
data[str(id)] = segments[id]
msg = wrap_payload(data)
#print(len(msg)) # this line is used for CC graphs
udpSocket.sendto(msg, addr)
connection.sendall(str.encode("download complete\n"))
def newconnection(connection, id):
"""
this method initiates new client connection,
before connection has established the server manages the client's information.
every clients has 2 new threads, one for listening and one for sending.
:param connection:
:param id:
:return:
"""
data = connection.recv(2048)
if not data:
print("not data!")
exit_thread()
name = data.decode('utf-8')
while name in clients.values():
name += str(id)
clients[id] = name
print(name, "connected with id ", id) # print to the cmd that a new client has connected
pms[name] = []
msgs.append(name+"["+str(id)+"] connected\n")
receive_req_from_client(connection, name, id)
def get_users(connection):
"""
simple get users method sends the client list of online clients
:param connection:
:return:
"""
msg = "---ONLINE USERS---\n"
connection.sendall(str.encode(msg))
for i in clients:
msg = clients.get(i)+"["+str(i)+"]\n"
connection.sendall(str.encode(msg))
def get_files(connection):
"""
simple get files method sends the clients list of available files for download.
:param connection:
:return:
"""
files = [f for f in listdir("files") if isfile(join("files", f))]
if len(files)==0:
msg = "---NO FILES AVAILABLE---\n"
connection.sendall(str.encode(msg))
else:
msg = "---AVAILABLE FILES---\n"
connection.sendall(str.encode(msg))
i = 1
for f in files:
msg = str(i)+". "+f+"\n"
i+=1
connection.sendall(str.encode(msg))
def receive_req_from_client(connection, name, id):
"""
Every client has a thread working on this method.
It listens to requests from the client.
:param connection:
:param name:
:param id:
:return:
"""
start_new_thread(send_msg_to_client, (connection, name, id))
connected = True
while connected:
try:
data = connection.recv(2048)
if not data:
print("problem at "+str(id)+", did he disconnect?")
connected = False
decoded_data = data.decode('utf-8')
# print("got ", decoded_data, " from ", name, id)
if decoded_data == "set_msg_all":
data = connection.recv(2048)
msg = name + "["+str(id)+"]: " + data.decode('utf-8')+"\n"
msgs.append(msg)
elif decoded_data == "set_msg":
data = connection.recv(2048)
decoded_data = data.decode('utf-8')
splited_msg = decoded_data.split()
target = splited_msg[0]
decoded_data = decoded_data[len(target)+1:]
msg = "|PM| " + name + "[" + str(id) + "]: " + decoded_data + "\n"
elif decoded_data == "download_file":
data = connection.recv(2048)
decoded_data = data.decode('utf-8')
files = [f for f in listdir("files") if isfile(join("files", f))]
if decoded_data[1:] not in files:
msg = decoded_data[1:]+" not available\n"
#pms[name].append(msg)
connection.sendall(str.encode(msg))
else:
msg = "establishing frUDP connection..\n"
#pms[name].append(msg)
connection.sendall(str.encode(msg))
start_new_thread(frUDP, (connection, decoded_data[1:]))
elif "set_msg_all" in decoded_data:
decoded_data = decoded_data[11:]
msg = name + "["+str(id)+"]: " + decoded_data+"\n"
msgs.append(msg)
elif "set_msg" in decoded_data:
decoded_data = decoded_data[7:]
splited_msg = decoded_data.split()
target = splited_msg[0]
decoded_data = decoded_data[len(target)+1:]
msg = "|PM| " + name + "[" + str(id) + "]: " + decoded_data + "\n"
if target in pms.keys():
pms[name].append(msg)
pms[target].append(msg)
elif target.isdecimal():
if int(target) in clients.keys():
pms[name].append(msg)
targetemp = clients.get(int(target))
pms[targetemp].append(msg)
else:
msg = target+" not online\n"
pms[name].append(msg)
elif "download_file" in decoded_data:
#data = connection.recv(2048)
decoded_data = decoded_data[13:]
files = [f for f in listdir("files") if isfile(join("files", f))]
if decoded_data[1:] not in files:
msg = decoded_data[1:]+" not available\n"
#pms[name].append(msg)
connection.sendall(str.encode(msg))
else:
msg = "establishing frUDP connection..\n"
#pms[name].append(msg)
connection.sendall(str.encode(msg))
start_new_thread(frUDP, (connection, decoded_data[1:]))
elif decoded_data == "set_msg_all":
data = connection.recv(2048)
msg = name + "["+str(id)+"]: " + data.decode('utf-8')+"\n"
msgs.append(msg)
elif decoded_data == "disconnect":
connected = False
elif decoded_data == "get_users":
get_users(connection)
elif decoded_data == "get_files":
get_files(connection)
else:
print("error got weird command: "+ decoded_data)
except:
print("problem at user "+str(id)+", did he disconnect?")
break
print(id, "disconnected \n")
clients.pop(id)
connection.close()
msg = name+"["+str(id)+"] disconnected\n"
msgs.append(msg)
def send_msg_to_client(connection, name, id):
"""
Every client has one thread working on this method.
It sends messages to the client.
:param connection:
:param name:
:param id:
:return:
"""
prevmsgs = []
prevpms = []
while id in clients.keys():
if len(msgs)!=len(prevmsgs):
for i in msgs:
if i not in prevmsgs:
connection.sendall(str.encode(i))
prevmsgs = msgs.copy()
if pms.get(name) is not None:
if len(pms.get(name))!=len(prevpms):
for i in pms.get(name):
if i not in prevpms:
connection.sendall(str.encode(i))
prevpms = pms.get(name).copy()
"""
Always listen to new connection, once a new client has connected create 2 new threads for it.
"""
id = 0
running = True
while running:
Client, address = serverSocket.accept()
print('Connected to: ' + address[0] + ':' + str(address[1]))
id += 1
start_new_thread(newconnection, (Client, id))
serverSocket.close()