-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient.py
More file actions
209 lines (187 loc) · 9.04 KB
/
client.py
File metadata and controls
209 lines (187 loc) · 9.04 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
# client.py
import socket
import random
import threading
import time
from packet import TCPPacket
# --- تنظیمات ---
SERVER_HOST = '127.0.0.1'
SERVER_PORT = 12345
TIMEOUT_SECONDS = 5
MSS = 100
WINDOW_SIZE = 5
RETRANSMISSION_TIMEOUT = 3
# --- متغیرهای سراسری ---
window_lock = threading.Lock()
base = 0
next_seq_num = 0
in_flight_packets = {}
retransmission_timer = None
program_active = True
duplicate_ack_count = 0
last_acked_seq = -1
# ... (توابع fast_retransmit, resend_oldest_packet, start_timer, stop_timer)
def fast_retransmit(sock, dest_addr):
"""تابع برای انجام بازارسال سریع."""
with window_lock:
seq_to_resend = base
if seq_to_resend in in_flight_packets:
packet, _ = in_flight_packets[seq_to_resend]
sock.sendto(packet.to_bytes(), dest_addr)
print(f"--> [FAST RETRANSMIT] ... Retransmitting seq={seq_to_resend}")
global duplicate_ack_count
duplicate_ack_count = 0
def resend_oldest_packet(sock, dest_addr):
"""تابع ارسال مجدد بر اساس تایماوت."""
with window_lock:
if not in_flight_packets: return
oldest_seq = base
if oldest_seq in in_flight_packets:
packet, _ = in_flight_packets[oldest_seq]
sock.sendto(packet.to_bytes(), dest_addr)
print(f"--> [TIMEOUT RETRANSMIT] ... Resending seq={oldest_seq}")
in_flight_packets[oldest_seq] = (packet, time.time())
start_timer(sock, dest_addr)
def start_timer(sock, dest_addr):
global retransmission_timer
if retransmission_timer: retransmission_timer.cancel()
retransmission_timer = threading.Timer(RETRANSMISSION_TIMEOUT, resend_oldest_packet, args=[sock, dest_addr])
retransmission_timer.start()
def stop_timer():
global retransmission_timer
if retransmission_timer:
retransmission_timer.cancel()
retransmission_timer = None
def receiver_thread(sock):
"""رشته دریافتکننده با قابلیت تشخیص RST"""
global base, duplicate_ack_count, last_acked_seq, program_active
print("[Receiver Thread] Started.")
while program_active:
try:
sock.settimeout(1.0)
packet_bytes, dest_addr = sock.recvfrom(4096)
packet = TCPPacket.from_bytes(packet_bytes)
# اگر بسته RST دریافت شد، برنامه را متوقف کن
if packet.flags['RST']:
print(f"!!! [Receiver Thread] Received RST packet from server: {packet} !!!")
print("!!! Connection is being forcibly closed by the server. Shutting down. !!!")
program_active = False
break # خروج از حلقه
if packet.flags['ACK']:
with window_lock:
if packet.ack_num > base:
print(f"[Receiver Thread] Received a CUMULATIVE ACK, moving window base to {packet.ack_num}")
base = packet.ack_num
duplicate_ack_count = 0
last_acked_seq = -1
acked_seqs = [seq for seq in in_flight_packets if seq < base]
for seq in acked_seqs: del in_flight_packets[seq]
if in_flight_packets: start_timer(sock, dest_addr)
else: stop_timer()
elif packet.ack_num == base:
# ... (منطق ACK تکراری)
if last_acked_seq != packet.ack_num:
last_acked_seq = packet.ack_num
duplicate_ack_count = 1
else:
duplicate_ack_count += 1
print(f"[Receiver Thread] Received a DUPLICATE ACK for seq={packet.ack_num}. Count: {duplicate_ack_count}")
if duplicate_ack_count == 3:
fast_retransmit(sock, dest_addr)
except socket.timeout: continue
except Exception as e:
# از چاپ خطا در زمان بسته شدن عادی جلوگیری میکنیم
if program_active:
print(f"[Receiver Thread] Error: {e}")
break
print("[Receiver Thread] Stopped.")
def main():
global base, next_seq_num, program_active, duplicate_ack_count, last_acked_seq
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
program_active = True # ریست کردن وضعیت در ابتدای هر اجرا
# --- فاز ۱: Handshake ---
print("\n[Client - Handshake]")
# ... (Handshake)
client_seq = random.randint(10001, 20000)
syn_packet = TCPPacket(src_port=0, dest_port=SERVER_PORT, seq_num=client_seq, ack_num=0, syn=1)
client_socket.sendto(syn_packet.to_bytes(), (SERVER_HOST, SERVER_PORT))
client_port = client_socket.getsockname()[1]
syn_packet.src_port = client_port
print(f"1. Sent SYN: {syn_packet}")
client_socket.settimeout(TIMEOUT_SECONDS)
syn_ack_data, _ = client_socket.recvfrom(4096)
syn_ack_packet = TCPPacket.from_bytes(syn_ack_data)
print(f"2. Received SYN-ACK: {syn_ack_packet}")
client_ack = syn_ack_packet.seq_num + 1
client_seq += 1
ack_packet = TCPPacket(src_port=client_port, dest_port=SERVER_PORT, seq_num=client_seq, ack_num=client_ack, ack=1)
client_socket.sendto(ack_packet.to_bytes(), (SERVER_HOST, SERVER_PORT))
print(f"3. Sent Final ACK: {ack_packet}")
print("Connection Established.")
base = client_seq
next_seq_num = client_seq
in_flight_packets.clear()
duplicate_ack_count = 0
last_acked_seq = -1
# --- فاز ۲: انتقال داده ---
print("\n[Phase 2: Data Transfer]")
receiver = threading.Thread(target=receiver_thread, args=(client_socket,))
receiver.start()
message = "Standard message from the main client."
data_to_send = message.encode('utf-8')
total_len = len(data_to_send)
while base < client_seq + total_len and program_active: # <<< شرط برای RST ?
# ... (کد ارسال داده )
with window_lock:
while len(in_flight_packets) < WINDOW_SIZE:
if (next_seq_num - client_seq) < total_len:
start_index = next_seq_num - client_seq
end_index = start_index + MSS
segment = data_to_send[start_index:end_index]
data_packet = TCPPacket(src_port=client_port, dest_port=SERVER_PORT, seq_num=next_seq_num, ack_num=client_ack, payload=segment)
client_socket.sendto(data_packet.to_bytes(), (SERVER_HOST, SERVER_PORT))
in_flight_packets[next_seq_num] = (data_packet, time.time())
if base == next_seq_num: start_timer(client_socket, (SERVER_HOST, SERVER_PORT))
next_seq_num += len(segment)
else: break
time.sleep(0.1)
while in_flight_packets and program_active: time.sleep(1)
# اگر برنامه به خاطر RST متوقف نشده، ادامه بده
if program_active:
print("\nAll data has been sent and acknowledged successfully.")
# --- فاز ۳: پایان اتصال ---
print("Stopping receiver thread to handle termination exclusively...")
program_active = False
receiver.join()
stop_timer()
print("\n[Phase 3: Connection Termination]")
# ... ( پایان اتصال)
try:
fin_packet = TCPPacket(src_port=client_port, dest_port=SERVER_PORT, seq_num=next_seq_num, ack_num=0, fin=1)
client_socket.sendto(fin_packet.to_bytes(), (SERVER_HOST, SERVER_PORT))
got_ack_for_my_fin, got_fin_from_server, server_fin_seq_num = False, False, -1
client_socket.settimeout(TIMEOUT_SECONDS)
while not (got_ack_for_my_fin and got_fin_from_server):
packet_bytes, _ = client_socket.recvfrom(4096)
packet = TCPPacket.from_bytes(packet_bytes)
if packet.flags['RST']: raise ConnectionResetError("RST received during termination")
if packet.flags['ACK'] and packet.ack_num == next_seq_num + 1: got_ack_for_my_fin = True
if packet.flags['FIN']: got_fin_from_server = True; server_fin_seq_num = packet.seq_num
final_ack_num = server_fin_seq_num + 1
final_ack_packet = TCPPacket(src_port=client_port, dest_port=SERVER_PORT, seq_num=next_seq_num + 1, ack_num=final_ack_num, ack=1)
client_socket.sendto(final_ack_packet.to_bytes(), (SERVER_HOST, SERVER_PORT))
print("Connection terminated successfully.")
except (socket.timeout, ConnectionResetError) as e:
print(f"Client: Issue during termination: {e}")
# پاکسازی نهایی
if not receiver.is_alive():
pass
else:
program_active = False
receiver.join()
stop_timer()
print("Shutting down...")
client_socket.close()
print("Client is shut down.")
if __name__ == "__main__":
main()