-
Notifications
You must be signed in to change notification settings - Fork 0
/
serverGame.py
275 lines (253 loc) · 12 KB
/
serverGame.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
"""
The module for the server class of the file transfer application using UDP
"""
import sys
import os
import time
from typing import List
from math import ceil
from socket import timeout
from lib.parserGame import parse_args_game
from lib.connection import Connection
from lib.segment import Segment
from lib.constants import DEFAULT_IP, SEGMENT_SIZE, PAYLOAD_SIZE, SYN_FLAG, SYN_ACK_FLAG, ACK_FLAG, TIMEOUT_LISTEN, FIN_ACK_FLAG
from lib.tictactoe import TicTacToe
from lib.crc16 import crc16
class ServerGame:
"""
The server class of the file transfer application using UDP
The server should be able to:
1. Establishes a three-way handshake connection with the server
2. Send the file to the client
"""
def __init__(self) -> None:
args = parse_args_game(True)
broadcast_port = args
self.conn = Connection(broadcast=broadcast_port, as_server=True)
self.segment = Segment()
self.segment_list : List[Segment] = []
self.client_list = []
self.tic_tac_toe = TicTacToe()
def listen_for_clients(self) :
print("[ INFO ] Listening for clients")
while True:
try :
segment, client_addr = self.conn.listen_segment()
client_ip, client_port = client_addr
self.client_list.append(client_addr)
print(f"[ INFO ] Received connection request from client: {client_ip}:{client_port}")
answer = input("[ PROMPT ] Do you want to add more clients? (y/n) ")
while not (answer.lower() in ["y", "n"]):
print("[ ERROR ] Invalid input")
answer = input("[ PROMPT ] Do you want to add more clients? (y/n) ")
if answer.lower() == "n":
print("\n[ INFO ] The following clients will be served:")
for idx, client in enumerate(self.client_list):
print(f"[ INFO ] {idx+1}. {client[0]}:{client[1]}")
print()
break
except timeout:
print("[ TIMEOUT ] Timeout while listening for client, exiting")
break
def three_way_handshake(self, client_addr):
"""
Establishes a three-way handshake connection with the client
1. Receive SYN from client
2. Send SYN-ACK to client
3. Receive ACK from client
"""
print(
f"[ INFO ] [Client {client_addr[0]}:{client_addr[1]}] Initiating three-way handshake"
)
self.segment.set_flag(["SYN"])
while True:
if self.segment.get_flag() == SYN_FLAG:
print(
f"[ INFO ] [Client {client_addr[0]}:{client_addr[1]}] sent SYN to server"
)
header = self.segment.get_header()
header["seq"] = 0
header["ack"] = 0
self.segment.set_header(header)
self.conn.send(self.segment.to_bytes(), *client_addr)
try:
data, _ = self.conn.listen_segment()
self.segment = Segment.from_bytes(data)
except timeout:
print(
f"[ TIMEOUT ] [Client {client_addr[0]}:{client_addr[1]}] ACK response timeout, resending SYN"
)
elif self.segment.get_flag() == SYN_ACK_FLAG:
print(
f"[ INFO ] [Client {client_addr[0]}:{client_addr[1]}] received SYN-ACK from server"
)
print(
f"[ INFO ] [Client {client_addr[0]}:{client_addr[1]}] sent ACK to server"
)
header = self.segment.get_header()
header["seq"] = 1
header["ack"] = 1
self.segment.set_header(header)
self.segment.set_flag(["ACK"])
self.conn.send(self.segment.to_bytes(), *client_addr)
break
else:
print(
f"[ INFO ] [Client {client_addr[0]}:{client_addr[1]}] is waiting for file already, ending three-way handshake"
)
break
print(
f"[ INFO ] [Client {client_addr[0]}:{client_addr[1]}] Three-way handshake established"
)
def initiate_transfer(self):
for client in self.client_list:
self.three_way_handshake(client)
def start_game(self) :
'''
Mulai permainan sebagai pemain pertama (X)
'''
maxIdx = len(self.client_list)
for idx, client in enumerate(self.client_list):
print(f"[ INFO ] {idx+1}. {client[0]}:{client[1]}")
chosenClient = int(input("Choose a client to play with. Type the number of the client from the list."))
choiceValid = chosenClient <= maxIdx and chosenClient > 0
while(not(choiceValid)) :
chosenClient = int(input("Client number not valid. Type the number of the client from the list."))
choiceValid = chosenClient <= maxIdx and chosenClient > 0
while True :
self.tic_tac_toe.board = self.tic_tac_toe.create_board()
currClient = self.client_list[chosenClient-1]
print("Client valid. Starting game as player X. Gets first turn.")
gameOver = self.tic_tac_toe.check_game_over()
while(not(gameOver)) :
self.tic_tac_toe.print_board()
selfMove = input("Where do you want to place your piece ? Type the number of one of the empty squares.")
isValidMove = self.tic_tac_toe.play(int(selfMove))
while (not(isValidMove)) :
selfMove = input("Square is not valid. Type the number of one of the empty squares.")
isValidMove = self.tic_tac_toe.play(int(selfMove))
self.send_move(int(selfMove), currClient)
gameOver = self.tic_tac_toe.check_game_over()
if (gameOver) :
break
oppMove = self.receive_move(currClient)
print(oppMove)
self.tic_tac_toe.play(oppMove)
gameOver = self.tic_tac_toe.check_game_over()
self.tic_tac_toe.print_board()
print("The winner is", self.tic_tac_toe.get_winner())
self.segment.set_flag([])
self.close_connection(currClient)
break
def shutdown(self) :
"""Shutdown the server"""
self.conn.close()
def close_connection(self, client) :
self.segment.set_flag([])
fin_acked = False
time_limit = time.time() + TIMEOUT_LISTEN
while not fin_acked:
try :
self.segment.set_payload(bytes())
self.segment.set_flag(["FIN", "ACK"])
self.conn.send(self.segment.to_bytes(), client[0], client[1])
response, client_addr = self.conn.listen_segment()
self.segment = Segment.from_bytes(response)
if (client_addr == client and self.segment.get_flag() == ACK_FLAG) :
print(f'[Client {client[0]}:{client[1]}] Received ACK for FIN from client')
fin_acked = True
elif (client_addr != client) :
print(f'[Client {client[0]}:{client[1]}] Received message from wrong client')
else :
print(f'[Client {client[0]}:{client[1]}] Received non-ACK flag')
except :
if time.time() > time_limit:
print(
f"[ WARNING ] [Client {client[0]}:{client[1]}] [Timeout] Server waited too long, connection closed."
)
break
print(f'[Client {client[0]}:{client[1]}] Connection timed out. Resending FIN message')
client_fin_acked = False
time_limit = time.time() + TIMEOUT_LISTEN
while (not client_fin_acked) :
try :
response, client_addr = self.conn.listen_segment()
self.segment = Segment.from_bytes(response)
if (client_addr == client and self.segment.get_flag() == FIN_ACK_FLAG) :
print(f'[Client {client[0]}:{client[1]}] Received FIN request from client. Sending ACK and shutting down connection.')
self.segment.set_payload(bytes())
self.segment.set_flag(["ACK"])
self.conn.send(self.segment.to_bytes(), client[0], client[1])
client_fin_acked = True
elif (client_addr != client) :
print(f'[Client {client[0]}:{client[1]}] Received message from wrong client')
else :
print(f'[Client {client[0]}:{client[1]}] Received non-FIN-ACK flag')
except TimeoutError :
if time.time() > time_limit:
print(
f"[ WARNING ] [Client {client[0]}:{client[1]}] [Timeout] Server waited too long, connection closed."
)
break
print(f'[Client {client[0]}:{client[1]}] Connection timed out. Waiting again.')
def send_move(self, move : int, currClient) :
header = self.segment.get_header()
header["seq"] = 0
header["ack"] = 0
self.segment.set_header(header)
message = int.to_bytes(move,64,'big')
self.segment.set_payload(message)
self.segment.set_checksum(crc16(message))
self.conn.send(self.segment.to_bytes(), currClient[0], currClient[1])
moveAcked = False # Menentukan apakah gerakan sudah diterima server atau belum
while(not(moveAcked)) :
try :
print("Before server send")
data, client_addr = self.conn.listen_segment()
self.segment = Segment.from_bytes(data)
print("After server send")
if client_addr == currClient and self.segment.get_flag() == ACK_FLAG :
print("Move successfully sent to client")
self.segment.set_flag([])
moveAcked = True
else :
print("Received message from wrong address, listening again")
except :
print("Error sending move, sending again")
header = self.segment.get_header()
header["seq"] = 0
header["ack"] = 0
self.segment.set_header(header)
self.segment.set_payload(message)
self.segment.set_checksum(crc16(message))
self.conn.send(self.segment.to_bytes(), currClient[0], currClient[1])
def receive_move(self, currClient) :
moveReceived = False
while(not(moveReceived)) :
try :
data, server_addr = self.conn.listen_segment()
self.segment = Segment.from_bytes(data)
if server_addr == currClient :
if self.segment.is_valid() :
print("Move successfully received from client")
moveReceived = True
oppMove = int.from_bytes(self.segment.get_payload(), "big")
header = self.segment.get_header()
header["ack"] = header["seq"] + 1
header["seq"] = 0
self.segment.set_header(header)
self.segment.set_payload(bytes())
self.segment.set_flag(["ACK"])
self.conn.send(self.segment.to_bytes(), currClient[0], currClient[1])
return(oppMove)
else :
print("Received message is corrupted, listening again")
else :
print("Received message from wrong address, listening again")
except :
print("Error receiving move, waiting again")
if __name__ == "__main__":
SERVER = ServerGame()
SERVER.listen_for_clients()
SERVER.initiate_transfer()
SERVER.start_game()