-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient_scenario3.py
More file actions
195 lines (170 loc) · 9.18 KB
/
client_scenario3.py
File metadata and controls
195 lines (170 loc) · 9.18 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
# client_scenario3.py
# این کلاینت برای نمایش (Fast Retransmit) طراحی شده است.
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 = 10
# --- متغیرهای سراسری ---
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
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] 3 Duplicate ACKs received! Retransmitting packet with 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] Timeout occurred! Resending packet 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):
"""رشته دریافتکننده با منطق جدید برای شمارش ACK تکراری"""
global base, duplicate_ack_count, last_acked_seq
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)
if packet.flags['ACK']:
with window_lock:
# اگر ACK جدید است
if packet.ack_num > base:
print(f"[Receiver Thread] Received a CUMULATIVE ACK, moving window base to {packet.ack_num}")
base = packet.ack_num
# ریست کردن شمارنده ACK تکراری
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()
# اگر ACK تکراری است
elif packet.ack_num == base:
# اگر این اولین ACK تکراری برای این seq است، شمارنده را شروع کن
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: break
print("[Receiver Thread] Stopped.")
def main():
global base, next_seq_num, program_active
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# --- فاز ۱: 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("Connection Established.")
base = client_seq
next_seq_num = client_seq
# --- فاز ۲: انتقال داده برای نمایش بازارسال سریع ---
print("\n[Phase 2: Data Transfer - Fast Retransmit Scenario]")
receiver = threading.Thread(target=receiver_thread, args=(client_socket,))
receiver.start()
# یک پیام که حداقل به ۴ سگمنت تقسیم شود (با MSS=100)
message = "This very long message is specifically designed to test the fast retransmit mechanism by losing the first segment and sending many more."
data_to_send = message.encode('utf-8')
total_len = len(data_to_send)
is_first_data_packet = True
while base < client_seq + total_len:
with window_lock:
while next_seq_num < base + (WINDOW_SIZE * MSS):
if (next_seq_num - client_seq) < total_len:
start, end = next_seq_num - client_seq, next_seq_num - client_seq + MSS
segment = data_to_send[start:end]
data_packet = TCPPacket(src_port=client_port, dest_port=SERVER_PORT, seq_num=next_seq_num, ack_num=client_ack, payload=segment)
if is_first_data_packet:
print(f"!!! SCENARIO 3: SIMULATING PACKET LOSS. NOT SENDING packet with seq={next_seq_num} !!!")
is_first_data_packet = False
else:
client_socket.sendto(data_packet.to_bytes(), (SERVER_HOST, SERVER_PORT))
print(f"Client: Sent data packet seq={next_seq_num}")
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.2) # کمی تاخیر برای مشاهده بهتر
while in_flight_packets: time.sleep(1)
# --- فاز ۳: پایان اتصال با لاگ دقیق ---
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))
print(f"Client: Sent FIN packet: {fin_packet}")
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['ACK'] and packet.ack_num == next_seq_num + 1: got_ack_for_my_fin = True; print(f"Client: Received ACK for its FIN: {packet}")
if packet.flags['FIN']: got_fin_from_server = True; server_fin_seq_num = packet.seq_num; print(f"Client: Received FIN from server: {packet}")
final_ack_num, final_ack_packet = server_fin_seq_num + 1, TCPPacket(src_port=client_port, dest_port=SERVER_PORT, seq_num=next_seq_num + 1, ack_num=server_fin_seq_num + 1, ack=1)
client_socket.sendto(final_ack_packet.to_bytes(), (SERVER_HOST, SERVER_PORT))
print(f"Client: Sent final ACK: {final_ack_packet}")
print("Connection terminated successfully.")
except socket.timeout: print("Client: Timeout during connection termination.")
finally: print("Shutting down..."); client_socket.close(); print("Client is shut down.")
if __name__ == "__main__":
main()