-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver_2.py
More file actions
231 lines (205 loc) · 9.16 KB
/
server_2.py
File metadata and controls
231 lines (205 loc) · 9.16 KB
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
'''
This module defines the behaviour of server in your Chat Application
'''
import sys
import getopt
import socket
import util
class Server:
'''
This is the main Server Class.
'''
def __init__(self, dest, port, window):
self.server_addr = dest
self.server_port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.settimeout(None)
self.sock.bind((self.server_addr, self.server_port))
self.clients = {}
def start(self):
'''
Main loop.
continue receiving messages from Clients and processing it
'''
while True:
data, addr = self.sock.recvfrom(1024)
self.process_packet(data, addr)
def process_packet(self, data, addr):
try:
packet_type, seq_num, message, received_checksum = util.parse_packet(data.decode())
calculated_checksum = util.validate_checksum(message)
if received_checksum != calculated_checksum:
print(f"Checksum mismatch, packet from {addr} ignored.")
return
# process the message
if packet_type == "start":
self.client_info[addr] = seq_num
print(f"Connection initiated from {addr}.")
elif type == 'data':
expected_seq = self.client_info[addr] + 1
if seq_num == expected_seq:
print(f"Data received from {addr}: {message}")
self.client_info[addr] += 1
else:
print(f"Unexpected sequence number from {addr}. Expected: {expected_seq}, got: {seq_num}")
# split the message into parts
message_parts = message.split()
# get the command
command = message_parts[0]
if command == "join":
# get the username
username = message_parts[2]
self.join(username, addr)
elif command == "request_users_list":
# check if the address is in the clients dictionary
if addr not in self.clients:
# send error message to the server
print("Error: Address not recognized")
else:
# request the list of active users
self.request_users_list(self.clients[addr], addr)
elif command == "send_message":
try:
# get the number of active users
num_active_users = int(message_parts[3])
# get the list of active users
active_users = message_parts[4:4 + num_active_users]
# get the message text
text = ' '.join(message_parts[4 + num_active_users:])
# get the sender
sender = self.clients.get(addr, "Unknown")
# create the forward message
forward_message_content = f"{sender}: {text}"
forward_message = util.make_message('msg', 4, forward_message_content)
# print the message to the server
print(f"msg: {sender}")
# forward the message to the active users
self.send_message(sender, active_users, forward_message)
except IndexError:
# handle index error
pass
except ValueError:
# handle value error
pass
elif command == "disconnect":
try:
# get the username
username = message_parts[2]
# check if the address is in the clients dictionary
if addr in self.clients:
# delete the client from the clients dictionary
del self.clients[addr]
# print the disconnect message to the server
print(f"disconnected: {username}")
else:
# print the error message to the server
print(f"disconnect attempted by non-existent or already disconnected user: {username}")
except IndexError:
# handle index error
pass
else:
# handle unknown message
self.err_unknown_message(addr)
elif packet_type == "end":
print(f"Connection closed from {addr}.")
del self.client_info[addr]
# Sending ACK
ack_msg = util.make_packet('ack', self.client_info[addr] + 1, '', calculated_checksum)
self.sock.sendto(ack_msg.encode(), addr)
except KeyboardInterrupt:
self.sock.close()
except Exception as e:
self.sock.close()
def join(self, username, addr):
'''
This method is used to join the server
'''
# check if server is full
if len(self.clients) >= util.MAX_NUM_CLIENTS:
# send error message to the client
error_message = util.make_message("ERR_SERVER_FULL", 2)
self.sock.sendto(util.make_packet("data", 0, error_message).encode(), addr)
# print disonnect message to server
print("disconnected: server full")
elif username in self.clients.values():
# send error message to the client if username is already taken
error_message = util.make_message("ERR_USERNAME_UNAVAILABLE", 2)
self.sock.sendto(util.make_packet("data", 0, error_message).encode(), addr)
# print disonnect message to server
print("disconnected: username not available")
else:
# add the client to the clients dictionary
self.clients[addr] = username
# send successful join message to the client
print(f"join: {username}")
def request_users_list(self, username, addr):
'''
This method is used to request the list of active users
'''
# get the list of users sorted A - Z
user_list = ', '.join(sorted(self.clients.values()))
# send the list of users to the client
response_msg = util.make_message("RESPONSE_USERS_LIST", 3, user_list)
response_packet = util.make_packet("data", 0, response_msg).encode()
self.sock.sendto(response_packet, addr)
# print the request_users_list message to the server
print(f"request_users_list: {username}")
def send_message(self, sender, active_users, message):
'''
This method is used to send a message to active users
'''
# send the message to the active users
for user in active_users:
# check if the user is in the clients dictionary
if user in self.clients.values():
# get the address of the recipient
recipient_address = [addr for addr, username in self.clients.items() if username == user]
# send the message to the recipient
for rec_addr in recipient_address:
self.sock.sendto(util.make_packet("data", 0, message).encode(), rec_addr)
else:
# print the error message to the server
print(f"msg: {sender} to non-existent user {user}")
def err_unknown_message(self, addr):
'''
This method is used to handle errors
'''
error_message = util.make_message("ERR_UNKNOWN_MESSAGE", 2)
self.sock.sendto(util.make_packet("data", 0, error_message).encode(), addr)
if addr in self.clients:
# delete the client from the clients dictionary
del self.clients[addr]
# print the disconnect message to the server
print("disconnected: server received an unknown message")
if __name__ == "__main__":
def helper():
'''
This function is just for the sake of our module completion
'''
print("Server")
print("-p PORT | --port=PORT The server port, defaults to 15000")
print("-a ADDRESS | --address=ADDRESS The server ip or hostname, defaults to localhost")
print("-w WINDOW | --window=WINDOW The window size, default is 3")
print("-h | --help Print this help")
try:
OPTS, ARGS = getopt.getopt(sys.argv[1:],
"p:a:w", ["port=", "address=","window="])
except getopt.GetoptError:
helper()
exit()
PORT = 15000
DEST = "localhost"
WINDOW = 3
for o, a in OPTS:
if o in ("-p", "--port="):
PORT = int(a)
elif o in ("-a", "--address="):
DEST = a
elif o in ("-w", "--window="):
WINDOW = a
SERVER = Server(DEST, PORT,WINDOW)
try:
SERVER.start()
except (KeyboardInterrupt, SystemExit):
exit()