-
Notifications
You must be signed in to change notification settings - Fork 0
/
RxP.py
467 lines (423 loc) · 17 KB
/
RxP.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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
#!/usr/bin/env python
from rxpHeader import RxPHeader
from rxpTimer import RxPTimer
from rxpWindow import RxPWindow
from threads import SendThread
from socket import *
from collections import deque
from crc import crc16xmodem
import unicodedata, sys
class RxP:
dataMax = 255 # total bytes in a packet
# refer to rxpHeader class for more info
def __init__(self, hostAddress, emuPort, hostPort, destPort, filename):
self.netEmuPort = emuPort
self.hostAddress = hostAddress
self.hostPort = hostPort
self.destPort = destPort
self.socket = socket(AF_INET, SOCK_DGRAM)
self.socket.bind((self.hostAddress, self.hostPort))
self.header = RxPHeader(hostPort, destPort, 0, 0)
self.cntBit = 0 # stands for connection state(3 way handshake)
self.getBit = 0 # get file
self.postBit = 0 # post file
self.rxpWindow = RxPWindow()
self.rxpTimer = RxPTimer()
self.buffer = deque() # buffer to store data
self.output = None # output file
self.recvFileIndex = 0 # current index of receiving file
self.threads = [] # supports multiple get request
# When user type in "connect" command Establish handshake connection with
# host by sending SYN messages. Handling the time out situation
# cntBit = 0 : listening for connection
# cntBit = 1 : received first SYN = 1 packet
def connect(self):
print 'Start to connect'
self.header.cnt = True
self.header.syn = True
self.header.seqNum = 0
self.send(None)
self.rxpTimer.start()
print 'Send first SYN segment with SYN = 1'
while self.cntBit == 0:
if self.rxpTimer.isTimeout():
self.header.syn = True
self.header.seqNum = 0
self.send(None)
print 'Resend first SYN segment with SYN = 1'
self.rxpTimer.start()
while self.cntBit == 1:
if self.rxpTimer.isTimeout():
self.header.syn = False
self.header.seqNum = 1
self.send(None)
print 'Resend segment with SYN = 0'
self.rxpTimer.start()
self.header.cnt = False
print '>>>>>>>>>>>Connection established<<<<<<<<<<<<<'
# When receive "disconnect" command,it will close connection.
# cntBit = 2 : connection established.
# cntBit = 3 : closing wait
def close(self):
self.header.cnt = True
self.header.fin = True
self.header.seqNum = 0
self.send(None)
print 'Send first FIN segment with FIN = 1'
self.rxpTimer.start()
while self.cntBit == 2:
if self.rxpTimer.isTimeout():
self.header.fin = True
self.header.seqNum = 0
self.send(None)
print 'Resend first FIN segment with FIN = 1'
self.rxpTimer.start()
while self.cntBit == 3:
if self.rxpTimer.isTimeout():
self.header.fin = False
self.header.seqNum = 1
self.send(None)
print 'Resend first FIN segment with FIN = 0'
self.rxpTimer.start()
self.header.cnt = False
print '>>>>>>>>>Connection Closed<<<<<<<<<<<<'
self.reset()
# Listening the incoming request including connect request, get, post, and
# data action by checking the received packet contents
def listen(self, event):
print 'start to listen'
while True and not event.stopped():
self.socket.settimeout(1)
try:
recvPacket, address = self.socket.recvfrom(1024)
except IOError:
continue
packet = bytearray(recvPacket)
if self.validateChecksum(packet):
tempHeader = self.getHeader(packet)
if tempHeader.cnt:
print 'Received control packet'
self.recvCntPkt(packet)
elif tempHeader.get:
print 'Received get packet'
self.recvGetPkt(packet)
elif tempHeader.post:
print 'Received post packet'
self.recvPostPkt(packet)
elif tempHeader.dat:
print 'Received data packet'
self.recvDataPkt(packet)
else:
print 'Received corrupted data, packet dropped.'
# reset all setting
def reset(self):
self.rxpWindow = RxPWindow()
self.rxpTimer = RxPTimer()
self.cntBit = 0
self.getBit = 0
self.postBit = 0
self.buffer = deque()
self.recvFileIndex = 0
self.output = None
# client uses getFile method to get a file from server side protocol sends
# the requested file to client side
def getFile(self, filename):
if self.cntBit == 2:
nameBytes = bytearray(filename)
self.header.get = True
self.header.seqNum = 0
self.send(nameBytes)
self.header.get = False
print 'Sending Get request'
self.rxpTimer.start()
while self.getBit == 0:
if self.rxpTimer.isTimeout():
self.header.get = True
self.header.seqNum = 0
self.send(nameBytes)
self.header.get = False
print 'Resend Get request'
self.rxpTimer.start()
print 'Start to receive file'
else:
print 'No connection found'
# Protocol send incoming data into Datagrams through UDP socket the data
# need to be add check sum before sending
def send(self, data):
print 'sending seq#: %d' % self.header.seqNum
self.header.ack = False
datagram = self.pack(self.header.getHeader(), data)
datagram = self.addChecksum(datagram)
self.socket.sendto(datagram, (self.hostAddress, self.netEmuPort))
# Getting header's ack number and add check sum to this ack number then
# send new ACK through UDP socket
def sendAck(self):
print 'acking num: %d' % self.header.ackNum
self.header.ack = True
datagram = self.addChecksum(self.header.getHeader())
self.socket.sendto(datagram, (self.hostAddress, self.netEmuPort))
# Getting the header information from received data
def getHeader(self, datagram):
tmpHeader = RxPHeader()
tmpHeader.headerFromBytes(datagram)
return tmpHeader
# Packing header array and data array into a new array, so that we can send
# this new data to the UDP socket
def pack(self,header, data):
if data:
result = header + data
return result
else:
return header
# Sending file to the server.
def postFile(self, filename, event):
if self.cntBit == 2:
nameBytes = bytearray(filename)
self.header.post = True
self.header.seqNum = 0
self.send(nameBytes)
self.header.post = False
print 'Sending Post request'
self.rxpTimer.start()
while self.postBit == 0 and not event.stopped():
if self.rxpTimer.isTimeout():
self.header.post = True
self.header.seqNum = 0
self.send(nameBytes)
self.header.post = False
print 'Resend Post request'
self.rxpTimer.start()
if event.stopped():
print 'Post file interrupted'
return
try:
readFile = open(filename, "rb")
except:
print ("file not found. Please type in correct filename")
sys.exit()
fileBytes = bytearray(readFile.read())
bufferSize = RxP.dataMax - RxPHeader.headerLen
fileSize = len(fileBytes)
fileIndex = 0
self.rxpTimer.start()
while (fileIndex < fileSize or len(self.buffer) > 0) and not event.stopped():
if self.rxpTimer.isTimeout():
self.rxpWindow.nextToSend = self.rxpWindow.startWindow
self.rxpTimer.start()
for i in range(len(self.buffer)):
if fileIndex >= fileSize:
if i == len(self.buffer) - 1:
self.header.end = True
seq = self.rxpWindow.nextToSend
self.header.seqNum = seq
self.header.dat = True
try:
self.send(self.buffer[i])
except:
print ("Corruption or Reodring rate too high, connection fails")
sys.exit()
self.header.dat = False
self.header.end = False
self.rxpWindow.nextToSend = seq + 1
if self.rxpWindow.nextToSend <= self.rxpWindow.endWindow and fileIndex < fileSize:
data = []
if fileIndex + bufferSize > fileSize:
data = fileBytes[fileIndex:fileSize]
else:
data = fileBytes[fileIndex:fileIndex + bufferSize]
fileIndex += bufferSize
if fileIndex >= fileSize:
self.header.end = True
seq = self.rxpWindow.nextToSend
self.header.seqNum = seq
self.header.dat = True
self.send(data)
self.header.dat = False
self.header.end = False
self.rxpWindow.nextToSend = seq + 1
self.buffer.append(data)
readFile.close()
self.postBit = 0
self.getBit = 0
self.header.end = False
print '>>>>>>>>>>>File transfer completed<<<<<<<<<<<<'
if event.stopped():
print 'Post file interrupted'
else:
print 'No connection found'
# Handle received data transmission packet.
def recvDataPkt(self, packet):
tmpHeader = self.getHeader(packet)
if tmpHeader.ack:
print 'Received Data ACK Num: %d' % tmpHeader.ackNum
if tmpHeader.ackNum == self.rxpWindow.startWindow:
self.rxpTimer.start()
self.rxpWindow.startWindow += 1
self.rxpWindow.endWindow += 1
self.buffer.popleft()
else:
if self.output == None:
print 'Output is not ready'
else:
if self.recvFileIndex == tmpHeader.seqNum:
content = packet[RxPHeader.headerLen:]
self.output.write(content)
self.recvFileIndex += 1
self.output.flush()
if tmpHeader.end:
self.output.close()
print '>>>>>>>>>>>>File transfer completed<<<<<<<<<<<<<'
seq = tmpHeader.seqNum
if self.recvFileIndex > seq:
self.header.ackNum = seq
elif self.recvFileIndex < seq:
if self.recvFileIndex != 0:
self.header.ackNum = self.recvFileIndex - 1
else:
self.header.ackNum = 0
self.header.dat = True
self.sendAck()
self.header.dat = False
# Handle get file packet
def recvGetPkt(self, packet):
tmpHeader = self.getHeader(packet)
seq = tmpHeader.seqNum
self.header.ackNum = seq
if tmpHeader.ack:
self.getBit = 1
else:
if self.getBit == 0:
content = packet[RxPHeader.headerLen:]
uniFilename = self.bytesToString(content)
filename = unicodedata.normalize('NFKD', uniFilename).encode('utf-8','ignore')
self.getBit = 1
sendTread = SendThread(self, filename)
self.threads.append(sendTread)
sendTread.start()
self.header.get = True
self.sendAck()
self.header.get = False
# Handle post file packet
def recvPostPkt(self, packet):
tmpHeader = self.getHeader(packet)
seq = tmpHeader.seqNum
self.header.ackNum = seq
if self.postBit == 0:
if tmpHeader.ack:
self.postBit = 1
else:
content = packet[RxPHeader.headerLen:]
filename = self.bytesToString(content)
try:
self.output = open("./down/" + filename, "ab")
except:
print ("file not found. Please type in correct filename")
sys.exit()
self.header.post = True
print 'sending post ack'
self.sendAck()
self.header.post = False
# receive connection establishment packet
# 3 way handshake
# closing wait
def recvCntPkt(self, packet):
tmpHeader = self.getHeader(packet)
seq = tmpHeader.seqNum
self.header.ackNum = seq
if self.cntBit == 0:
if tmpHeader.syn:
print 'Received connection request SYN segment with SYN = 1'
self.header.cnt = True
self.sendAck()
self.cntBit = 1
elif self.header.syn and tmpHeader.ack:
self.header.syn = False
self.header.seqNum = 1
self.send(None)
print 'Received first SYN ack, sending second msg[SYN=0]'
self.cntBit = 1
elif not tmpHeader.fin and not tmpHeader.ack:
print 'server received SYN from client, and sent back ACK to client'
self.header.cnt = True
self.sendAck()
self.header.cnt = False
elif self.cntBit == 1:
if not tmpHeader.ack and not tmpHeader.syn:
self.cntBit = 2
self.sendAck()
self.header.cnt = False
print '>>>>>>>>>>>>Connection established<<<<<<<<<<<<<'
if tmpHeader.seqNum == 0 and tmpHeader.syn:
print 'Second SYN initialization'
self.header.cnt = True
self.sendAck()
self.header.cnt = False
if tmpHeader.ack:
self.cntBit = 2
elif self.cntBit == 2:
if tmpHeader.fin:
print 'Received connection closing request with FIN = 1'
self.header.cnt = True
self.sendAck()
self.cntBit = 3
elif self.header.fin and tmpHeader.ack:
self.header.fin = False
self.header.seqNum = 1
self.send(None)
print 'Received first FIN ack, sending second packet with FIN = 0'
self.cntBit = 3
elif not tmpHeader.ack and not tmpHeader.syn:
self.header.cnt = True
self.sendAck()
self.header.cnt = False
elif self.cntBit == 3:
if not tmpHeader.ack and not tmpHeader.fin:
self.cntBit = 0
self.sendAck()
self.header.cnt = False
self.reset()
print '>>>>>>>>>Connection Close<<<<<<<<<<<'
elif not tmpHeader.seqNum and tmpHeader.fin:
self.header.cnt = True
self.sendAck()
self.header.cnt = False
elif tmpHeader.ack:
self.cntBit = 0
# set the window size for protocol
def setWindowSize(self, windowSize):
if self.cntBit == 2:
self.rxpWindow.windowSize = windowSize
print 'The window size is set to: %d' % windowSize
else:
print 'Please initialize connection.'
# Convert the ASCII byte[] data into String
def bytesToString(self, data):
return data.decode('utf-8')
# Before sending the packet, we have to add a check sum field into each
# packet to make sure the correction of data
def addChecksum(self, packet):
data = ''
packet[14] = 0
packet[15] = 0
for byte in packet:
data += str(byte)
checksum = crc16xmodem(data)
packet[14] = checksum >> 8
packet[15] = checksum & 0xFF
return packet
# Using this check sum function to check every received packet's corruption
def validateChecksum(self, packet):
correct = False
data = ''
firstB = packet[14]
secondB = packet[15]
packet[14] = 0
packet[15] = 0
for byte in packet:
data += str(byte)
checksum = crc16xmodem(data)
msb = checksum >> 8
lsb = checksum & 0xFF
if msb == firstB and lsb == secondB:
correct = True
return correct