TCP Reliability — ack, retransmission, RTO, fast recovery
TCP Reliability and Retransmission
হারানো packet, উল্টো ক্রম, ভুল সন্দেহ — wire-এর সব অরাজকতার উপর দাঁড়িয়ে TCP কীভাবে ordered byte-stream promise রাখে; EWMA থেকে SACK পর্যন্ত প্রতিটা যন্ত্রাংশ trace সহ।
আগে এটা বুঝি
আগের লেসনে connection-এর জন্ম দেখলাম — handshake শেষে দুই পক্ষ একমত যে client গুনছে 1001 থেকে, server 5001 থেকে। কিন্তু এখনই আসল প্রশ্ন: IP-র জগৎ তো best-effort — packet হারায়, উল্টে আসে, দুইবার আসে। এই অরাজকতার উপর TCP কীভাবে দাঁড়িয়ে promise রাখে: “তুমি যে ক্রমে পাঠিয়েছ, সেই ক্রমে, একবারই, সবকিছু পাবে।”?
উত্তরের হৃৎপিণ্ড একটা মাত্র ধারণা: ACK-clock। Receiver ঘুম থেকে ডেকে বলে “এই পর্যন্ত পেলাম” — আর সেই ডাকটাই sender-এর ঘড়ির কাঁটা এগোয়। Packet হারালে ডাক আসে না; ডাকের নীরবতাই সংকেত। TCP-র পুরো reliability যন্ত্রটা এই নীরবতা পড়ার তিনটা আলাদা কৌশলের সমষ্টি:
- Timeout (RTO) — যথেষ্ট নীরবতার পরে নিজেই সন্দেহ করা
- Duplicate ACK — receiver-এর বারবার একই অভিযোগ শুনে তাড়াতাড়ি সন্দেহ করা
- SACK — receiver নিজেই map এঁকে দেওয়া “যা আছে তা-ই আছে, ফাঁক এখানে”
আজকের লেসনের শেষে আপনি নিজের লেখা simulator-এ দেখবেন — fast retransmit ঠিক কত সেকেন্ড বাঁচায়, আর কেন RTO-র অপেক্ষা internet- এ মৃত্যুর শাস্তি।
মূল ধারণা
Byte numbering আর cumulative ACK
Handshake-এ দেখেছি: seq = এই segment-এর প্রথম byte-এর নম্বর, ack = যা পেয়েছি তার পরের expected byte। এই “+1”-এর ভেতরেই লুকিয়ে আছে সবচেয়ে গুরুত্বপূর্ণ শব্দ: cumulative।
ACK = 5001 বলার মানে শুধু “5000 পেয়েছি” নয় — “ISN+1 থেকে
5000 পর্যন্ত সবকিছু, কোনো ফাঁক ছাড়া, পেয়েছি।” একটা সংখ্যা
দুই হাজার byte-এর প্রাপ্তি-সনদ। সস্তা, নির্ভরযোগ্য — কিন্তু
একটা দাম আছে: 5001-এর আগে ফাঁক থাকলে 5200 পেয়েও বলতে পারবে
না “5200 পেয়েছি”। Out-of-order segment-গুলো তাই receiver-এর
buffer-এ অপেক্ষা করে — ফাঁক ভরলে সঙ্গে সঙ্গে সারি সাজিয়ে
application-এ যোগ দেয়।
Sliding window — flow control-এর যন্ত্র
Sender-এর জগৎ তিনটা পয়েন্টারে ভাগ:
১ ──────[ sent & acked ]──────┬──[ sent, unacked ]──┬──[ পাঠানো যাবে ]──► ∞
↑ base ↑ next-to-send
(lowest unacked)
পাঠানো যাবে শর্তে: next-to-send < base + rwndReceiver প্রতিটা ACK-এর সাথে window field-এ ঘোষণা করে: “আমার receive-buffer-এ এখনো এইটুকু জায়গা আছে।” Application ধীর হলে buffer ভরে, window ছোট হয়, sender থমকে যায় — zero-window হলে পুরো বন্ধ। তখন sender মাঝে মাঝে ছোট probe পাঠায় (persist timer) — “এখনো জায়গা হলো?” — নইলে দুই পক্ষ চিরকাল অপেক্ষায় আটকে যেত।
RTT মাপা → RTO বানানো: EWMA গণিত
Retransmission timeout কত হওয়া উচিত? ছোট হলে spurious retransmit
(আসলে packet পথে আছে, আমরা তাড়াহুড়োয় আবার পাঠালাম); বড় হলে
সত্যিকারের loss-এর পরে অযৌক্তিক অপেক্ষা। সমাধান: RTT মেপে
smoothed অনুমান রাখা। RFC 6298-এর সূত্র — প্রতি measurement
R'-এর পরে (α = ⅛, β = ¼):
পড়ার নিয়ম: SRTT হলো গড়, RTTVAR হলো গড় থেকে ছড়ানোর মাপ — আর RTO হলো গড় plus four deviations — অর্থাৎ “স্বাভাবিক ঝাঁকুনির চার গুণের বেশি নীরব হলে তবেই সন্দেহ করব।” এই সূত্রটা আসলে EWMA — Exponentially Weighted Moving Average — ঠিক সেই ভারিত গড়, যেখানে পুরনো মান সূচকীয় হারে বিবর্ণ হয়।
Timeout আসলেই লাগলে backoff: প্রতিবার RTO × 2। যুক্তি: timeout মানে network অসুস্থ; অসুস্থ network-এ ধারাবাহিক retransmit ঢেলে দেওয়া আগুনে ঘি ঢালা। Congestion collapse-এর আগের যুগের শিক্ষা (পরের লেসনের মূল কাহিনি)।
Fast retransmit — ৩টা duplicate ACK-র গণিত
RTO-র সমস্যা: এটা অন্ধ — নীরবতা দেখলেই অপেক্ষা করে RTO পর্যন্ত (typical 200ms+)। কিন্তু packet হারালে receiver যা করে সেটা লক্ষ্য করুন:
sender: seg-1 seg-2✗ seg-3 seg-4 seg-5
receiver: ok (হারাল) গর্ত! গর্ত! গর্ত!
ACK-2 — ACK-2 ACK-2 ACK-2 ← তিনবার একই অভিযোগ!প্রতিটা out-of-order arrival-এ receiver আবার সেই expected সংখ্যা পাঠায় — duplicate ACK। তিনটা dup-ACK মানে: তিনটা পরের segment সফলভাবে পৌঁছেছে, অথচ ওই একটা ফাঁক — তাহলে packet হারিয়েছে, দেরিতে আসছে না (reordering-এর tolerance হিসেবেই ১ নয়, ৩)। Sender অপেক্ষা না করে সঙ্গে সঙ্গে সেই একটা segment পুনঃপাঠায় — fast retransmit। খরচ: ~1 RTT। RTO-র খরচ: একটা পূর্ণ RTO।
SACK — receiver নিজেই মানচিত্র আঁকে
Cumulative ACK-র জন্মগত অন্ধত্ব: এক ACK-তে একটাই সংখ্যা। SACK option সেই ফাঁক ভরে: ACK-এর সাথে তালিকা —
left edge right edge মানে
4501 5000 "4501–5000 আমার কাছে আছে"
5201 5600 "5201–5600-ও আছে"
(implicit: 5001–5200 নেই — ওইটাই ফাঁক!)Sender তখন শুধু ফাঁকটাই পুনঃপাঠায় — বাকি সব যা আছে তা রেখে দিয়ে।
Go-back-N বনাম Selective repeat — তত্ত্বের কাঠামো
Textbook-এর দুই pole:
| Go-back-N | Selective Repeat | |
|---|---|---|
| Loss-এ কী হয় | ফাঁকের পরে সব পুনঃপাঠায় | শুধু ফাঁকটাই |
| Receiver buffer | ফাঁকের পরের সব ফেলে দেয় | জমিয়ে রাখে |
| Sender-এর state | ন্যূনতম | প্রতি segment-এর timer |
| বাস্তব TCP | ছাড়া SACK: N-ঘেঁষা | SACK-সহ: SR-ঘেঁষা |
আধুনিক OS-এ SACK default-on — অর্থাৎ আজকের TCP বাস্তবে selective repeat-এর দিকেই ঝোঁকা, যদিও cumulative ACK-র ভাষায় কথা বলেই।
ভেতরে কী ঘটছে
Dup-ACK কবে পাঠায়, কবে পাঠায় না
সূক্ষ্ম নিয়মগুলো যা capture-এ গুলিয়ে ফেলে:
- Out-of-order segment আসলেই কেবল dup-ACK (same expected)
- In-order segment আসলে নতুন cumulative ACK — এতে dup গণনা reset
- Window-full বা delayed-ACK policy-তে কখনো ACK-ই আসে না — Nagle
- delayed-ACK interaction-এর কুখ্যাত 40ms pause এখান থেকেই
- Dup-ACK-গুলো নিজে কোনো data consume করে না — শুধু সংকেত
Spurious retransmission — ভুল সন্দেহের দাম
Fast retransmit-এর ৩-dup নিয়ম reordering tolerate করে, পুরোপুরি নয়। Mobile network-এ handover, Wi-Fi roaming, multi-path routing — packet ২–৩ hop পেছনে পড়ে যেতে পারে। তখন sender ভাবে lost, পুনঃপাঠায় — কিন্তু original-ও পরে পৌঁছায়। Receiver দুইবার একই data পায় (dup detection-এ ফেলে দেয়, তবু bandwidth নষ্ট), আর worse: RTT sample-গুলো দূষিত হয়। Karn’s algorithm: retransmitted segment- এর RTT sample কোনোদিন estimation-এ ঢোকে না — কারণ সেই ACK কোন copy-র উত্তর তা বলা যায় না।
Zero-window আর persist timer
Receiver window=0 ঘোষণা করলে sender থেমে যায় — কিন্তু পুরো থেমে থাকলে বিপদ: receiver পরে window খুলে জানাল, সেই update packet হারালে দুই পক্ষ চিরকাল অপেক্ষা (deadlock!)। Sender তাই persist timer-এ বাঁধা — মাঝে মাঝে 1-byte window probe পাঠায়; receiver-এর প্রতিটা উত্তরে বর্তমান window আসেই। নীরব network-এও clock তখন বাজতে থাকে।
উদাহরণ
Timeline ১ — সব ঠিকঠাক (loss নেই)
t=0.000 C→S seg-1..5 (window ভরা) [seq 1001,1101,1201,1301,1401]
t=0.050 S পায় seg-1 → ACK-1101
t=0.050 S পায় seg-2 → ACK-1201 (cumulative!)
t=0.060 C ACK-1201 পায় → base=3, window slide → seg-6 পাঠায়
...প্রতিটা ACK base সামনে ঠেলে, window স্লাইড করে, নতুন data-র অনুমতি দেয় — ACK-clock চলছে: প্রতিটা received byte-এর উত্তরে এক byte পাঠানোর অনুমতি।
Timeline ২ — seg-2 হারাল, fast retransmit
t=0.000 C→S seg-1..5 (seg-2 পথে হারাল)
t=0.050 S: seg-1 পেল → ACK-1101
t=0.060 S: seg-3 পেল (গর্ত 1101!) → DUP-ACK-1101 (#1)
t=0.070 S: seg-4 পেল → DUP-ACK-1101 (#2)
t=0.075 C: DUP-ACK-1101 #1 পেল — অপেক্ষা...
t=0.080 S: seg-5 পেল → DUP-ACK-1101 (#3)
t=0.085 C: #3 পেল → ★ FAST RETRANSMIT seg-2
t=0.135 S: seg-2 পেল → buffer-এ 3,4,5 ছিল → সাথে সাথে ACK-1501
t=0.140 C: base=6 → seg-6..10 পাঠায় — সব normalমোট জরিমানা: প্রায় 1 RTT (০.০৫s)।
Timeline ৩ — একই loss, fast retransmit বন্ধ
t=0.000 C→S seg-1..5 (seg-2 হারাল)
t=0.06..0.09 DUP-ACK-1101 ×3 → C উপেক্ষা করে (fast off)
t=0.500 ⏱ RTO পুরো! → seg-2 পুনঃপাঠায়
t=0.550 S: seg-2 → ACK-1501
t=0.600 C: base=6 → বাকি সব...মোট জরিমানা: RTO পুরোটা (৫০০ms) — fast path-এর ~১০ গুণ। এই সংখ্যাটাই নিচের build-এ নিজের চোখে দেখবেন।
ছবিতে
স্বাভাবিক: ফাঁক (seg-2 lost): recovery-র পরে:
bytes: 1 2 3 4 5 6 ... bytes: 1 [2] 3 4 5 ... bytes: 1 2 3 4 5 6 ...
████████░░░░ █░█████░░░░ ██████████
base↑ base (stuck at 2) base→
next→ next→ next→
rwnd=4 window ভরা, কিছুই window স্লাইড,
(৪টা unacked পর্যন্ত) নতুন পাঠানো যাবে না আবার চলছেআর reliability-র স্তর-মানচিত্র:
- Dup-ACK storm~1 RTT-তে signal — সবচেয়ে দ্রুত, fast retransmit
- SACK blocksঠিক কোন gap — শুধু সেটাই পুনঃপাঠানো
- RTO timeoutশেষ ভরসা — সেকেন্ড-স্কেল, exponential backoff
- Connection abortবারবার backoff-এর পরেও নীরব → error app-এ
নিজে চালিয়ে দেখুন
dup-ACK আর RTO নিজের চোখে — capture পড়া ও (sudo হলে) loss ঢোকানো
ধাপ ১ — capture চালু করে ছোট transfer:
# terminal A (sudo লাগে; না থাকলে নিচের pre-made trace পড়ুন)
sudo tcpdump -i any -n -S 'tcp port 8443 and host 127.0.0.1'
# terminal B: ছোট server + transfer
python3 -m http.server 8443 &
curl -s http://127.0.0.1:8443/ >/dev/nullLocalhost-এ loss হয় না — তাই এখানে dup-ACK দেখবেন না; দেখবেন
in-order ACK-র cumulative এগোনো (ack মান লাফিয়ে লাফিয়ে বাড়ছে)।
Loss দেখতে হলে নিচের দুই path।
ধাপ ২ (sudo থাকলে) — netem দিয়ে loss ঢোকান:
# Linux: loopback-এ ২% packet loss + সামান্য delay
sudo tc qdisc add dev lo root netem delay 20ms loss 2%
curl -s -o /dev/null http://127.0.0.1:8443/bigfile
# capture-এ এখন খুঁজুন: একই ack মান ৩ বার, তারপর seq-এ পিছনে গিয়ে
# একটা segment retransmit
sudo tc qdisc del dev lo root # পরিষ্কার করতে ভুলবেন না!ধাপ ৩ (sudo ছাড়া) — lesson-provided trace পড়ুন:
নিচের excerpt-টা 2% loss-সহ transfer থেকে — হাতে annotate করুন:
12:00:01.101 IP .5000 > .8443: Flags [.] seq 14401:14801 ← seg-4
12:00:01.102 IP .8443 > .5000: Flags [.] ack 14401 ← seg-3 এর পরে? gap!
12:00:01.104 IP .5000 > .8443: Flags [.] seq 15201:15601 ← seg-5 (out of order)
12:00:01.105 IP .8443 > .5000: Flags [.] ack 14401 ← DUP #2
12:00:01.107 IP .5000 > .8443: Flags [.] seq 15601:16001 ← seg-6
12:00:01.108 IP .8443 > .5000: Flags [.] ack 14401 ← DUP #3!
12:00:01.109 IP .5000 > .8443: Flags [.] seq 14401:14801 ← ★ retransmit (একই seq!)
12:00:01.130 IP .8443 > .5000: Flags [.] ack 16401 ← লাফিয়ে 16401!পড়ার চাবি: ack 14401 চারবার — প্রথমবার স্বাভাবিক, পরের
তিনবার dup; তার ঠিক পরেই একই seq 14401:14801 আবার — fast
retransmit। শেষ ACK-র বিশাল লাফ (14401→16401) প্রমাণ — out-of-order
segment-গুলো buffer-এ অপেক্ষা করছিল, gap ভরতেই সব একসাথে
acknowledge হলো।
capture-এ হারানো segment-এর ঠিক ৩টা duplicate ACK আর তার পরে একটা retransmit দেখা যায় — theory-র ৩-dup নিয়ম wire-এ হুবহু; RTO-path-এ সেই জায়গায় শুধু দীর্ঘ নীরবতা।
নিজে বানান
ACK-clock simulator — fast retransmit vs RTO, সেকেন্ডে সেকেন্ড
- Discrete-event engine: heapq-তে (time, kind, payload)-র event
- Receiver: expected counter + out-of-order buffer + dup-ACK emission
- Sender: sliding window, ৩-dup fast retransmit, RTO backoff
- একই loss scenario দুই mode-এ চালান — fast ON vs OFF
- Timeline print + শেষ সময়ের তুলনা — পার্থক্যটাই লেসন
import heapq
NSEG = 10 # মোট segment (প্রতিটা 1 MSS ধরে)
WINDOW = 5 # sender window
ONE_WAY = 0.050 # 50 ms একমুখী → RTT 100 ms
RTO0 = 0.500 # initial timeout
DROP_FIRST = {2} # প্রথম পাঠে হারাবে এই segment-গুলো
REORDER = {4: 0.030} # extra delay (sec) — reorder demo
def run(fast_retx):
drop = set(DROP_FIRST)
events = [] # (time, kind, val)
def tx(seq, now, tag=""):
if seq in drop:
drop.discard(seq)
print(f"{now:7.3f} seg-{seq} পাঠানো হলো{tag} … ✗ পথে হারাল")
return
heapq.heappush(events, (now + ONE_WAY + REORDER.get(seq, 0),
"seg", seq))
print(f"{now:7.3f} seg-{seq} পাঠানো হলো{tag}")
print(f"\n=== mode: fast retransmit {'চালু' if fast_retx else 'বন্ধ'} ===")
expected, ooo = 1, set() # receiver side
base, nxt = 1, 1 # sender pointers
dup, rto_val, rto_dl = 0, RTO0, RTO0
finish = None
for s in range(1, min(WINDOW, NSEG) + 1): # initial window fill
tx(s, 0.0)
while finish is None:
head = events[0][0] if events else float("inf")
if rto_dl is not None and rto_dl <= head: # RTO আগে?
print(f"{rto_dl:7.3f} ⏱ RTO! seg-{base} পুনঃপ্রেরণ")
tx(base, rto_dl, tag="(timeout)")
rto_val *= 2 # exponential backoff
rto_dl += rto_val
continue
if not events:
break
now, kind, val = heapq.heappop(events)
if kind == "seg": # receiver-এ পৌঁছাল
if val == expected:
expected += 1
while expected in ooo:
ooo.discard(expected); expected += 1
heapq.heappush(events, (now + ONE_WAY, "ack", expected))
print(f"{now:7.3f} seg-{val} পৌঁছাল → ACK-{expected}")
else:
ooo.add(val)
heapq.heappush(events, (now + ONE_WAY, "ack", expected))
print(f"{now:7.3f} seg-{val} out-of-order "
f"(seg-{expected} নেই!) → DUP-ACK-{expected}")
else: # sender ACK পেল
if val > base:
base, dup = val, 0
rto_val = RTO0; rto_dl = now + rto_val
while nxt <= NSEG and nxt < base + WINDOW:
tx(nxt, now); nxt += 1
if base > NSEG:
finish = now
else: # duplicate
dup += 1
print(f"{now:7.3f} DUP-ACK-{val} পেল (#{dup})")
if fast_retx and dup == 3:
print(f"{now:7.3f} ★ FAST RETRANSMIT seg-{base}")
tx(base, now, tag="(fast)")
dup, rto_dl = 0, now + rto_val
return finish
t_fast = run(True)
t_slow = run(False)
print("\n=== ফলাফল ===")
print(f"fast retransmit সহ : {t_fast:.3f}s")
print(f"শুধু RTO-তে : {t_slow:.3f}s")
print(f"জরিমানার পার্থক্য : +{(t_slow - t_fast)*1000:.0f} ms")প্রত্যাশিত আকারের ফলাফল:
=== mode: fast retransmit চালু ===
0.000 seg-1 পাঠানো হলো
0.000 seg-2 পাঠানো হলো … ✗ পথে হারাল
...
0.085 ★ FAST RETRANSMIT seg-2
0.330 ACK-11 পেল
=== mode: fast retransmit বন্ধ ===
0.500 ⏱ RTO! seg-2 পুনঃপ্রেরণ
...
0.850 ACK-11 পেল
=== ফলাফল ===
fast retransmit সহ : 0.330s
শুধু RTO-তে : 0.850s
জরিমানার পার্থক্য : +520 msনিজে বাড়ান:
REORDERবাড়িয়ে এমন অবস্থা বানান যেখানে ৩-dup নিয়ম ভুল fast retransmit trigger করে (spurious) — দেখুন receiver তবু সঠিক stream বানায়, শুধু bandwidth নষ্ট হয়- SACK block report যোগ করুন — receiver dup-ACK-র সাথে ooo-set ছাপবে; sender শুধু gap পুনঃপাঠাবে
DROP_FIRST-এ পরপর দুই segment যোগ করুন — fast retransmit- এর পরেও দ্বিতীয় loss-এর জন্য আবার ৩-dup জমা দেখুন
বাস্তব সিস্টেমে
বাস্তব জগতে reliability যন্ত্র
Mobile handover-এর spurious storm। Cell tower বদলাতে গিয়ে কয়েকশো ms নীরবতা — sender-দের RTO আগুন ধরে, একসাথে retransmit ঢালে, নতুন cell ভরে যায় — আবার loss — আবার retransmit। Modern stack-দের RTO randomization আর pacing ঠিক এই thundering herd ভাঙতে।
Satellite link-এর long-fat problem। GEO satellite RTT ~600ms; window ছোট হলে throughput = window/RTT — গণিতগতভাবে সীমাবদ্ধ। Window scale option (handshake-এর options-এ) ঠিক এজন্যই — 16-bit field-কে 32-bit পর্যন্ত বাড়ায়।
Datacenter-এর micro-RTO। LAN-এ RTT 100μs; 200ms RTO তাহলে 2000× বেশি! DCTCP আর RACK-TLP (Linux-এর আধুনিক loss recovery) এজন্যই — tail-loss probe দিয়ে RTO-কে প্রায় অপ্রয়োজনীয় করে তোলে।
rsync/scp-এর “stall” গল্প। বড় transfer হঠাৎ সেকেন্ডের পর
সেকেন্ড থেমে আবার চলে — সাধারণত RTO backoff-chain (0.5→1→2→4s)
আর zero-window probing-এর সম্মিলিত নাটক; ss -ti-এর retrans
counter-ই অপরাধীর পরিচয়।
Karn’s algorithm প্রতিদিন। আপনার browser-এর প্রতিটা connection নীরবে এটা মানে — retransmit-এর পরে RTT sample discard, নইলে estimation ঘুরে যেত।
যে ভুলগুলো সবাই করে
“ACK-5000 মানে '5000-তম byte-টা এসেছে' — এতটুকুই জানা গেল।”
Cumulative মানে তার অনেক বেশি: ISN+1 থেকে 5000 পর্যন্ত সব byte পাওয়া গেছে — কোনো ফাঁক নেই। এই একটা সংখ্যা হাজার byte-এর receipt। উল্টো দিকটাও সত্যি: 5001 এবং 5200 পেয়েছি কিন্তু 5100 নেই — তবু ACK বলবে শুধু 5001-এর পর্যন্ত; বাকিটা বলতে হয় SACK option দিয়ে।
“RTO একটা fixed value — সব connection-এ 200ms।”
RTO per-connection, per-moment dynamic — SRTT+4·RTTVAR দিয়ে বারবার হিসাব হয়, loss-এ ২× করে backoff, recovery-র পরে আবার ছোট হয়। Satellite link-এ শুরুতেই সেকেন্ড-স্কেল, datacenter-এ sub-millisecond হওয়ার কথা। Fixed 200ms ধরে কথা বললে পরীক্ষার উত্তর ভুল, production debugging আরও ভুল।
“Retransmission মানে receiver-এ ভুল data পৌঁছাবে — duplicate হয়ে যাবে।”
Receiver-এর sequence-number dedup ঠিক এই জন্যই: দুইবার আসা segment-এর দ্বিতীয়টা নীরবে ফেলে দেয়, application কখনো দেখেই না। Spurious retransmit-এর খরচ bandwidth আর CPU — correctness নয়। Reliability model-টা exactly-once delivery, “at-least-once তারপর dedup” নয়।
“Flow control আর congestion control একই জিনিসের দুই নাম।”
Flow control receiver-এর স্বার্থে — “আমার buffer ছোট, ধীরে পাঠাও” (rwnd)। Congestion control network-এর স্বার্থে — “router-গুলো ভরে যাচ্ছে, ধীরে পাঠাও” (cwnd)। একটা endpoint-এর সাথে কথা বলে, অন্যটা path-এর সাথে। পরের লেসনের প্রথম পাঠটা এই distinction ভাঙাই।
বুঝেছেন কি না দেখুন
1Cumulative ACK-এ ack = 3000 — এই একটা সংখ্যা ঠিক কী কী দাবি
করে?
স্মরণ
ack = 3000 — এই একটা সংখ্যা ঠিক কী কী দাবি
করে?“ISN+1 থেকে 2999 পর্যন্ত সব byte পেয়েছি, ফাঁক নেই; পরের expected byte 3000।” একটা সংখ্যা সম্পূর্ণ prefix-এর receipt — 3000-এর পরের কোনো byte পাওয়ার তথ্য এতে নেই।
2SRTT = 100ms, RTTVAR = 20ms, G উপেক্ষাযোগ্য। নতুন measurement
R’ = 180ms এলে (α=⅛, β=¼) নতুন SRTT, RTTVAR আর RTO কত?
প্রয়োগ
SRTT ← (7/8)(100) + (1/8)(180) = 87.5 + 22.5 = 110ms। RTTVAR ← (3/4)(20) + (1/4)|110−180| = 15 + 17.5 = 32.5ms। RTO ← 110 + 4(32.5) = 240ms। লক্ষ্য করুন: একটা খারাপ sample গড়কে সামান্যই নড়ায়, কিন্তু deviation বেড়ে RTO দ্রুত বড় হয় — system সতর্ক হয়ে ওঠে।
3Fast retransmit কেন ১টা dup-ACK-তে আগুন ধরে না — ঠিক ৩-তে কেন?
যুক্তি
Reordering-এর জন্য জায়গা: packet কাগজে-কলমে হারায়নি, পথে পিছিয়ে আছে — তখনও ১–২টা dup-ACK আসতে পারে। ৩টা dup-ACK মানে অন্তত তিনটা পরবর্তী segment সফলভাবে পৌঁছেছে — ওই একটার এত পিছিয়ে থাকা প্রায় নিশ্চিতভাবে loss, না হলে অতি-ব্যতিক্রমী reorder। Trade-off: কম threshold = বেশি spurious retransmit, বেশি = দেরি। ৩ হলো ঐতিহাসিক sweet spot।
4RTT = 100ms, RTO হিসাব হয়েছে 250ms। Timeout টানা ৩বার লাগলে
চতুর্থ attempt-টা কত ms অপেক্ষা করবে?
প্রয়োগ
Exponential backoff: 250 → 500 → 1000 → 2000ms। প্রতিটা ব্যর্থ retransmission-এ দ্বিগুণ — network-কে সুস্থ হওয়ার নিঃশ্বাস দেওয়া। (Maximum-এর সীমা typical 64s; তার আগে না ফিরলে connection abort।)
5Receiver-এর application কোনো data পড়ছে না (busy loop-এ আটকে)।
TCP-র কোন যন্ত্র sender-কে থামাবে? আর সেই থামা অবস্থায় deadlock
কীভাবে এড়ানো হয়?
যুক্তি
Receive buffer ভরে গেলে receiver window=0 advertise করে — sender-এর effective window শূন্য, কিছুই পাঠাতে পারে না (flow control কাজ করছে)। Deadlock-ঝুঁকি: window খোলার সংবাদ বহনকারী packet হারালে দুই পক্ষই অপেক্ষায় — এড়ায় persist timer: sender মাঝে মাঝে 1-byte window probe পাঠায়, receiver-এর প্রতিটা উত্তরে বর্তমান window-ও আসে।
6আপনার protocol-এ একটা নতুন idea: “প্রতিটা packet-এর নিজস্ব timer,
যেটা প্রথমেই expire হবে শুধু সেটাই retransmit হবে” (selective
repeat-এর pure রূপ)। TCP কেন এটা করে না — state আর wire-এর
কোন খরচে?
ডিজাইন
Per-segment timer মানে in-flight প্রতিটা segment-এর জন্য আলাদা timer-state — kernel-এ লক্ষ connection × শত শত segment = বিশাল memory/compute। TCP-র পছন্দ: একটা timer (base-এর উপর) + dup-ACK heuristic — প্রায় সব case-এ যথেষ্ট, cost O(1)। SACK যোগ হওয়ায় selective behavior পাওয়া গেছে wire-এ সামান্য option-byte খরচে, timer-এর state ছাড়াই — engineering-এর classic “state-কে wire-এ দাবি করো” move।
এরপর কী
এরপর কী
আমরা দেখলাম TCP কীভাবে নিজের ভুল শোধরায়। কিন্তু একটা গভীর প্রশ্ন ঝুলে আছে: sender কি শুধু receiver-কেই খেয়াল করে? Path-এর router-গুলো ভরে গেলে কে বলবে?
পরের লেসন — Congestion Control:
- Flow control (receiver) বনাম congestion control (network) — min(rwnd, cwnd)-এর দুই জগৎ
- Slow start-এর বিস্ফোরক নাম-বিপরীত বৃদ্ধি, AIMD-র sawtooth, আর fairness-এর যুক্তি
- Reno → CUBIC → BBR: তিন প্রজন্মের দর্শন
- Bufferbloat আর ১৯৮৬-এর internet-collapse গল্পটা, যেটা সব শুরু করেছিল
আজকের simulator-এ window ছিল fixed — পরের লেসনে সেটাই জীবন্ত হবে।
আরও পড়ুন
- RFC 9293 — Transmission Control Protocol (updated spec) — W. Eddy (ed.), IETF · Sequence/acknowledgment semantics-এর authoritative source
- RFC 6298 — Computing TCP's Retransmission Timer — V. Paxson, M. Allman et al. · SRTT/RTTVAR/RTO সূত্রের মূল document — এই লেসনের গণিতের ভিত্তি
- RFC 2018 — TCP Selective Acknowledgment Options — M. Mathis, J. Mahdavi et al. · SACK option-এর specification — gap reporting কীভাবে কাজ করে