পাঠ ২৫ · ৫৭-এর মধ্যে · মডিউল ৫
Home / Courses / Computer Networks / সিকোয়েন্স ও ACK

TCP রিলায়েবল ডেলিভারি — সিকোয়েন্স নাম্বার ও ACK

TCP reliable delivery — sequence numbers & ACKs
৯ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • TCP সিকোয়েন্স নাম্বার কীভাবে প্রতিটি বাইট ট্র্যাক করে
  • কিউমুলেটিভ ACK কী ও কেন এটি গ্যাপে আটকে যায়
  • টাইমআউট-ভিত্তিক রিট্রান্সমিশন TCP-এর নির্ভরযোগ্যতার ভিত্তি কীভাবে গড়ে
  • Python দিয়ে আউট-অফ-অর্ডার চাংক পুনর্গঠন ও কিউমুলেটিভ ACK-এর একটি সিমুলেশন

১ · সিকোয়েন্স নাম্বার — প্রতিটি বাইটের ঠিকানা

L24-এ আমরা দেখেছি হ্যান্ডশেকের সময় ক্লায়েন্ট ও সার্ভার উভয়েই একটি প্রাথমিক সিকোয়েন্স নাম্বারSequence NumberTCP প্রতিটি ডেটা বাইটকে একটি ক্রমিক নাম্বার দেয় — এটি ব্যবহার করে রিসিভার মিসিং বা আউট-অফ-অর্ডার ডেটা শনাক্ত করে এবং সঠিক ক্রমে পুনর্গঠন করে। (ISN) প্রস্তাব করে। এরপর প্রতিটি প্রকৃত ডেটা বাইটকে সেই ISN থেকে গুনে একটি ক্রমিক নাম্বার দেওয়া হয়। যেহেতু IP-লেয়ার (M4) কোনো অর্ডারিং গ্যারান্টি দেয় না — প্যাকেট বিভিন্ন পথে বিভিন্ন সময়ে পৌঁছাতে পারে, এমনকি আউট-অফ-অর্ডার হয়েও — এই সিকোয়েন্স নাম্বারই একমাত্র উপায় যা দিয়ে রিসিভার মূল বাইট-স্ট্রিমটি সঠিক ক্রমে পুনর্গঠন করতে পারে।

২ · কিউমুলেটিভ ACK

রিসিভার একটি কিউমুলেটিভ ACKCumulative Acknowledgmentরিসিভার শুধু "পরবর্তী প্রত্যাশিত বাইট নাম্বার" পাঠায় — এর মানে তার আগ পর্যন্ত সব বাইট ধারাবাহিকভাবে সঠিকভাবে পাওয়া গেছে। মাঝে কোনো গ্যাপ থাকলে ACK আর এগোয় না। পাঠায় — একটি একক নাম্বার যার অর্থ "আমি N নাম্বার বাইট পর্যন্ত সব ধারাবাহিকভাবে সঠিকভাবে পেয়েছি, পরেরটা N+1 প্রত্যাশা করছি"। গুরুত্বপূর্ণ বিষয় — এই ACK শুধু ততদূর পর্যন্তই এগোয় যতদূর ডেটা কোনো গ্যাপ ছাড়া ধারাবাহিকভাবে এসেছে। যদি বাইট ০-১৯ ও ৪২-৪৯ পৌঁছে যায় কিন্তু ২০-৪১ এখনও না আসে, ACK আটকে থাকবে ২০-এ — এমনকি পরের চাংকগুলো বাফারে জমা থাকলেও।

নির্ভরযোগ্যতার মূল মেকানিজম

সেন্ডার প্রতিটি অ্যাকনলেজড-না-হওয়া ডেটার জন্য একটি টাইমার রাখে। নির্দিষ্ট সময়ের মধ্যে ACK না এলে, সেন্ডার ধরে নেয় সেই ডেটা (বা তার ACK) হারিয়ে গেছে এবং সেটি রিট্রান্সমিট করে। এই টাইমআউট-এবং-রিট্রান্সমিট চক্রটিই TCP-এর মূল নির্ভরযোগ্যতা গ্যারান্টি — সরাসরি বিপরীত L23-এর UDP-এর সাথে, যেখানে হারানো ডেটা চিরতরে হারিয়েই যায়, কোনো পুনঃচেষ্টা ছাড়াই।

Python
# আউট-অফ-অর্ডার আগমন ও পুনর্গঠন সিমুলেশন — টয় ইন-মেমরি ডেটা, কোনো প্রকৃত নেটওয়ার্ক নয়

original_message = "TCP গ্যারান্টি দেয় যে সব বাইট সঠিক ক্রমে পৌঁছাবে।"
chunk_texts = ["TCP গ্যারান্টি দেয় ", "যে সব বাইট ", "সঠিক ক্রমে ", "পৌঁছাবে।"]

# প্রতিটি চাংকের seq_number = মূল স্ট্রিমে তার শুরুর বাইট-পজিশন
seq = 0
ordered_chunks = []
for text in chunk_texts:
    ordered_chunks.append((seq, text))
    seq += len(text)

# নেটওয়ার্কে IP কোনো অর্ডার গ্যারান্টি দেয় না -- ইচ্ছাকৃতভাবে এলোমেলো ক্রমে "গ্রহণ" করা হলো
received_chunks = [ordered_chunks[2], ordered_chunks[0], ordered_chunks[3], ordered_chunks[1]]

print("আউট-অফ-অর্ডার গ্রহণ করা চাংক (sequence_number, data):")
for seq_num, data in received_chunks:
    print(f"  seq={seq_num}: {data!r}")

def reassemble(chunks):
    """সিকোয়েন্স নাম্বার অনুযায়ী সাজিয়ে মূল বাইট-স্ট্রিম পুনর্গঠন করে।"""
    ordered = sorted(chunks, key=lambda c: c[0])
    return "".join(data for _, data in ordered)

reassembled = reassemble(received_chunks)
print("\nপুনর্গঠিত বার্তা:", reassembled)
print("মূল বার্তার সাথে মিলছে?", reassembled == original_message)
assert reassembled == original_message

def cumulative_ack_progress(arrival_order):
    """যেসব বাইট এ পর্যন্ত ধারাবাহিকভাবে (গ্যাপ ছাড়া) পাওয়া গেছে, তার ঠিক পরের বাইট-নাম্বারকে
    cumulative ACK হিসেবে রিপোর্ট করে -- আগমনের ঠিক ক্রম অনুযায়ী, একটি একটি করে চাংক বাফারে যোগ করে।"""
    buffer = {}
    next_expected = 0
    log = []
    for seq_num, data in arrival_order:
        buffer[seq_num] = data
        while next_expected in buffer:
            next_expected += len(buffer[next_expected])
        log.append((seq_num, next_expected))
    return log

print("\nকিউমুলেটিভ ACK-এর অগ্রগতি (আগমনের ক্রম অনুযায়ী):")
for arrived_seq, ack_after in cumulative_ack_progress(received_chunks):
    print(f"  chunk seq={arrived_seq} এলো -> cumulative ACK (পরবর্তী প্রত্যাশিত বাইট) = {ack_after}")

    
লক্ষ্য করুন কিউমুলেটিভ ACK-এর আউটপুটে — প্রথম চাংক (seq=৩১) আসার পরও ACK থেকে যায় ০-তে (কারণ শুরুতে গ্যাপ আছে, বাইট ০-৩০ এখনও অনুপস্থিত)। seq=০ আসার পর ACK এগিয়ে যায় কিন্তু আবার একটি গ্যাপে (seq=২০ অনুপস্থিত) গিয়ে থেমে যায়। শুধুমাত্র সব গ্যাপ পূরণ হওয়ার পরেই (seq=২০ আসার পর) ACK পুরো বার্তার শেষ পর্যন্ত এক লাফে পৌঁছায়। এটিই কিউমুলেটিভ ACK-এর ঠিক আচরণ — এটি কখনও একটি গ্যাপ "ডিঙিয়ে" এগোয় না।
মূল কথা · Key takeaway

সিকোয়েন্স নাম্বার ও কিউমুলেটিভ ACK একসাথে TCP-কে একটি অবিশ্বাস্য, আউট-অফ-অর্ডার নেটওয়ার্ক (IP) এর উপর একটি নির্ভরযোগ্য, ধারাবাহিক বাইট-স্ট্রিম তৈরি করতে দেয় — কোনো ডেটা হারালে বা দেরি হলে সেন্ডার তা বুঝতে পারে (ACK না আসা বা গ্যাপে আটকে থাকা ACK দেখে) এবং প্রয়োজনে রিট্রান্সমিট করে। L26-এ আমরা দেখব রিসিভারের বাফার ক্ষমতা কীভাবে সেন্ডারকে কতটা ডেটা একসাথে পাঠানো যাবে তা নিয়ন্ত্রণ করে (ফ্লো কন্ট্রোল)।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ কিউমুলেটিভ ACK কেন একটি গ্যাপ "ডিঙিয়ে" এগোয় না, যদিও গ্যাপের পরের ডেটা ইতিমধ্যে বাফারে আছে?

কারণ কিউমুলেটিভ ACK-এর সংজ্ঞাই হলো "এই বাইট পর্যন্ত সব ধারাবাহিকভাবে সঠিক পেয়েছি" — যদি এটি গ্যাপ ডিঙিয়ে যেত, সেন্ডার ভুলভাবে ধরে নিত যে মাঝের হারানো অংশটিও পৌঁছেছে এবং সেটি আর কখনো রিট্রান্সমিট করত না, ফলে ডেটা স্ট্রিমে স্থায়ী একটি ফাঁক থেকে যেত। ACK গ্যাপে আটকে থাকাটাই সেন্ডারকে জানায় ঠিক কোন অংশ এখনো পুনরায় পাঠাতে হবে।

প্র ০২ যদি সেন্ডার একটি ডেটা ঠিকভাবে পাঠিয়ে থাকে কিন্তু তার ACK-টিই নেটওয়ার্কে হারিয়ে যায়, তাহলে কী হবে?

সেন্ডার টাইমআউটের পর সেই ডেটা আবার পাঠাবে (যদিও রিসিভার আসলে সেটি আগেই পেয়েছিল) — রিসিভার তখন একটি ডুপ্লিকেট বাইট-রেঞ্জ পাবে, যা সে সিকোয়েন্স নাম্বার দেখে চিনতে পারে ও উপেক্ষা করে, শুধু আরেকটি ACK আবার পাঠিয়ে দেয়। এভাবে ডেটা হারানো বা শুধু ACK হারানো — উভয় ক্ষেত্রেই একই "টাইমআউট + রিট্রান্সমিট" মেকানিজম সঠিকভাবে কাজ করে, যদিও মাঝেমধ্যে অপ্রয়োজনীয় ডুপ্লিকেট ডেটা পাঠানো হয়।

প্র ০৩ UDP-তে (L23) সিকোয়েন্স নাম্বার বা ACK নেই কেন — এটি কি একটি ডিজাইন ত্রুটি?

না, এটি একটি ইচ্ছাকৃত ডিজাইন সিদ্ধান্ত — সিকোয়েন্স নাম্বার ও ACK ট্র্যাক করা মানে অতিরিক্ত হেডার, স্টেট ও প্রসেসিং ওভারহেড। যেসব অ্যাপ্লিকেশনের জন্য গতি নির্ভরযোগ্যতার চেয়ে বেশি গুরুত্বপূর্ণ (L23-এর DNS/স্ট্রিমিং/ গেমিং উদাহরণ), সেখানে এই ওভারহেড এড়িয়ে UDP ব্যবহার করাটাই যুক্তিসঙ্গত পছন্দ।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলে received_chunks-এর ক্রম পরিবর্তন করে সম্পূর্ণ সঠিক ক্রমে (ordered_chunks হিসেবেই) সাজান এবং Run চেপে দেখুন cumulative_ack_progress-এর আউটপুট কীভাবে বদলায়।

    যদি চাংকগুলো একদম সঠিক ক্রমে (কোনো গ্যাপ ছাড়াই) আসে, তাহলে ACK প্রতিটি চাংক আসার সাথে সাথেই সমানুপাতিকভাবে এগোবে — কখনো থেমে থাকবে না, কারণ প্রতিটি নতুন চাংক ঠিক আগেরটির শেষ বাইট থেকেই শুরু হয়, ফলে কোনো গ্যাপ তৈরিই হয় না।

  2. পরীক্ষা করুন: received_chunks-এ শেষ চাংকটি (ordered_chunks[3], seq=৪২) সরিয়ে ফেলুন (যেন সেটি এখনো নেটওয়ার্কে পথে আছে) এবং দেখুন reassemble-এর ফলাফল কী হয়।

    reassemble শুধু যা আছে তাই জোড়া লাগাবে — ফলাফল হবে মূল বার্তার একটি অসম্পূর্ণ প্রিফিক্স (শেষ চাংক ছাড়া), এবং reassembled == original_message হবে False। বাস্তব TCP-তে এটাই সেই পরিস্থিতি যেখানে রিসিভারের ACK শেষ চাংকের সিকোয়েন্স নাম্বার পর্যন্ত পৌঁছাবে না, আর সেন্ডার টাইমআউটের পর সেটি রিট্রান্সমিট করবে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
L24 · TCP — কানেকশন ও থ্রি-ওয়ে হ্যান্ডশেক