This repository has been archived by the owner on Oct 2, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.py
173 lines (163 loc) · 7.33 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
166
167
168
169
170
171
172
173
import socket
import sys
from time import time
import util
from packet import Packet
def tcp(ip: str, port: int):
"""
:param ip: ip address to receive data from (not used)
:param port: port to listen to
:return:
"""
parcels = [] # list of data parcels
file_name = "" # file name
file = b'' # received data
timestamp = [0, 0] # start and finish timestamps
packet_count = 0 # total nr of packets
header_received = False # flag to check if header was received
# opening the socket using try-with-resources method
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
# bind to the receiving port to receive responses using that
s.bind(('', port))
# ready to receive, start listen
s.listen()
# start accepting packets
connection, _ = s.accept()
# try-with-resources for connection
with connection:
# loop until data fully received
while True:
# read 1024 bytes from the socket at each iteration
data = connection.recv(1024)
# if no data received, finish reading
if not data:
# store finishing timestamp
timestamp[1] = time()
break
# if headers not received, use the first data as header
if not header_received:
# split received data to use as header
# use the remaining data as the actual data, since tcp is a stream
try:
deciphered = util.decipher(data)
if deciphered:
file_name, timestamp[0], packet_count, data = deciphered
# set flag
header_received = True
except Exception:
pass
# append the received data to the parcel list
parcels.append(data)
# calculate total time spent at transfer
tcp_total = (timestamp[1] - timestamp[0]) * 1000
if packet_count != 0:
tcp_average = tcp_total / packet_count
else:
tcp_average = 0
print(f"TCP Packets Average Transmission Time: {tcp_average} ms")
print(f"TCP Communication Total Transmission Time: {tcp_total} ms")
# merge parcels
for parcel in parcels:
file += parcel
# write to file
with open(file_name, "wb") as file_pointer:
file_pointer.write(file)
def udp(ip: str, port: int):
"""
:param ip: ip address to receive data from (not used)
:param port: port to listen to
:return:
"""
parcels = [] # list of data parcels
file_name = "" # file name
file = b'' # received data
timestamp = [[0, 0]] # start and finish timestamps
packet_count = 0 # total nr of packets
header_received = False # flag to check if header was received
packets_received = [False] # list to check the first packet that did not arrive
# try-with-resources for connection
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
# bind to the port
s.bind(('', port))
# loop until all data is received
while True:
# read 2048 bytes of data every loop
data, client_address = s.recvfrom(2048)
# if data is valid, handle it, else wait until data arrives
if data:
# if header was not received, treat the first packet as header
if not header_received:
# try to decipher the data as header
header_try = util.decipher(data)
# if header can be parsed, then it is a valid header
# else wait until header
if header_try is not False:
# try to split header, since it might be corrupted
try:
file_name, timestamp[0][0], packet_count, data = header_try
# create the same header using the parsed data
new_header = util.generate(file_name, timestamp[0][0], packet_count)
# send it back as ACK
s.sendto(new_header.encode(), client_address)
# mark header received flag as True
header_received = True
# resize parcels list according to the packet count
parcels = [b""] * packet_count
# add first package receive time
timestamp[0][1] = time()
# resize timestamp list according to the packet count
timestamp_append = [[0, 0]] * packet_count
timestamp += timestamp_append
# resize packets received list according to the packet count
packets_received = [False] * packet_count
except Exception:
pass
else:
# header was received already, treat the data as parcel
r = Packet.read_data_for_udp(data)
# if the data is a valid parcel, use and ack it
if r is not False:
parcel_nr, timestamp_p, parcel, hashed = r
# if the parcel nr is not registered, treat it as new
if not packets_received[parcel_nr]:
# save parcel, timestamp, and mark parcel as received
parcels[parcel_nr] = parcel
timestamp[parcel_nr + 1] = timestamp_p, time()
packets_received[parcel_nr] = True
# send ACK
s.sendto(hashed.encode(), client_address)
# if all packets were received, break from the loop
if packets_received.count(False) == 0:
break
# merge parcels
for parcel in parcels:
file += parcel
# write to file
with open(file_name, "wb") as file_pointer:
file_pointer.write(file)
# calculate time differences
time_differences = [t[1] - t[0] for t in timestamp]
udp_average = (sum(time_differences) / len(time_differences)) * 1000
udp_total = (timestamp[-1][1] - timestamp[0][0]) * 1000
print(f"UDP Packets Average Transmission Time: {udp_average} ms")
print(f"UDP Communication Total Transmission Time: {udp_total} ms")
if __name__ == "__main__":
args = sys.argv
if len(args) != 3:
print("Usage: python server.py [udp-port] [tcp-port]")
exit()
else:
# read command line arguments
udp_port = args[1]
tcp_port = args[2]
# if non-integer values were given as port values, exit peacefully
try:
udp_port = int(udp_port)
tcp_port = int(tcp_port)
except Exception:
print("Port values must be integers", file=sys.stderr)
exit()
# get ip address of localhost
ip_address = socket.gethostbyname(socket.gethostname())
tcp(ip_address, tcp_port)
udp(ip_address, udp_port)