চূড়ান্ত প্রকল্প — সম্পূর্ণ নেটওয়ার্ক স্ট্যাক সিমুলেশন
এই পাঠে যা শিখবেন
- কীভাবে এই কোর্সের ভিন্ন ভিন্ন মডিউলে শেখা ধারণাগুলো (রাউটিং, ARP, CRC, সিকোয়েন্স নম্বর) একসাথে একটি সম্পূর্ণ প্রোটোকল স্ট্যাক তৈরি করে
- একটি প্রকৃত, স্বয়ং-যাচাইকারী (self-verifying) মাল্টি-লেয়ার এনক্যাপসুলেশন/ডিক্যাপসুলেশন সিমুলেশন Python-এ বাস্তবায়ন করা
- কেন একটি স্তরের ব্যর্থতা (যেমন CRC মিসম্যাচ) উপরের স্তরগুলো কখনো টেরই পায় না — লেয়ারিং/মডুলারিটির চূড়ান্ত প্রমাণ
- এই কোর্সের ৫৬টি আগের পাঠ কীভাবে একটি একক, সংহত মানসিক মডেলে একত্রিত হয়
১ · কেন এই পাঠ — কোর্সের সংশ্লেষণ
L01-এ আমরা একটি সরলীকৃত এনক্যাপসুলেশন/ডিক্যাপসুলেশন সিমুলেশন দিয়ে শুরু করেছিলাম — অ্যাপ্লিকেশন ডেটার উপর ধাপে ধাপে TCP, IP ও Ethernet হেডার বসানো, তারপর ঠিক বিপরীত ক্রমে সরিয়ে ফেলা। সেই সময় প্রতিটি "হেডার" ছিল নিছক একটি স্ট্রিং-ট্যাগ। এখন, ৫৬টি পাঠ পরে, আমরা জানি প্রতিটি হেডার প্রকৃতপক্ষে কী ধারণ করে এবং প্রতিটি স্তর ঠিক কী সিদ্ধান্ত নেয় — তাই এই ক্যাপস্টোনে আমরা একই প্যাটার্ন পুনর্ব্যবহার করছি, কিন্তু প্রতিটি ধাপে কোর্সের নির্দিষ্ট, প্রকৃত অ্যালগরিদম বসিয়ে।
সিকোয়েন্স নম্বর (L22, L25) — ডেটা ছোট সেগমেন্টে ভাগ, রিঅ্যাসেম্বলির জন্য প্রস্তুত।
Longest-prefix-match রাউটিং টেবিল লুকআপ (L14, L17) — গন্তব্যের পথ নির্ধারণ।
ARP-রিজলভড MAC অ্যাড্রেস (L11, L13) + CRC এরর-ডিটেকশন (L09)।
২ · সম্পূর্ণ round-trip সিমুলেশন
নিচের কোডে একটি একক বার্তা প্রেরক পাশে তিনটি স্তর দিয়ে এনক্যাপসুলেট হয়ে একটি "ফ্রেম" তৈরি করে, তারপর গ্রাহক পাশে ঠিক বিপরীত ক্রমে ডিক্যাপসুলেট ও পুনর্গঠিত হয়। প্রতিটি ফাংশনের ভেতরে মন্তব্যে কোন লেয়ার ও কোন আগের পাঠের ধারণা ব্যবহৃত হচ্ছে তা স্পষ্টভাবে চিহ্নিত করা আছে।
# ============================================================
# L57 ক্যাপস্টোন -- সম্পূর্ণ নেটওয়ার্ক স্ট্যাক সিমুলেশন
# L01-এর এনক্যাপসুলেশন/ডিক্যাপসুলেশন প্যাটার্নের একটি সম্পূর্ণ, বহু-স্তরের সম্প্রসারণ
# (in-memory ফেক ডেটা -- কোনো প্রকৃত নেটওয়ার্ক সকেট/প্যাকেট নয়, computer-networks/CLAUDE.md অনুযায়ী)
# ============================================================
app_message = "ABCL TECH নেটওয়ার্ক কোর্স সম্পন্ন হলো!"
# ---------- ট্রান্সপোর্ট লেয়ার (M5/L22 পোর্ট-মাল্টিপ্লেক্সিং, M5/L25 সিকোয়েন্স নম্বর) ----------
def segment_data(data, chunk_size=8):
"""অ্যাপ্লিকেশন ডেটাকে ছোট অংশে ভেঙে প্রতিটিতে একটি সিকোয়েন্স নম্বর যোগ করা হয়।"""
chunks = [data[i:i + chunk_size] for i in range(0, len(data), chunk_size)]
return [{"seq": i, "payload": c} for i, c in enumerate(chunks)]
# ---------- নেটওয়ার্ক লেয়ার (M4/L14 IP অ্যাড্রেসিং, M4/L17 longest-prefix-match রাউটিং) ----------
routing_table = [
("192.168.1.0", 24, "সরাসরি-সংযুক্ত"), # সবচেয়ে নির্দিষ্ট (/24) এন্ট্রি
("192.168.0.0", 16, "R1"), # কম নির্দিষ্ট (/16), ইচ্ছাকৃতভাবে ওভারল্যাপ করছে
("0.0.0.0", 0, "ডিফল্ট-গেটওয়ে"), # ক্যাচ-অল ডিফল্ট রুট
]
def ip_to_int(ip):
a, b, c, d = (int(x) for x in ip.split("."))
return (a << 24) | (b << 16) | (c << 8) | d
def lookup_route(dest_ip, table):
"""L17-এর longest-prefix-match -- একাধিক এন্ট্রি ম্যাচ করলে সবচেয়ে দীর্ঘ প্রিফিক্সটি জেতে।"""
dest_int = ip_to_int(dest_ip)
best_hop, best_len = None, -1
for prefix, plen, next_hop in table:
mask = (0xFFFFFFFF << (32 - plen)) & 0xFFFFFFFF if plen else 0
if (dest_int & mask) == (ip_to_int(prefix) & mask) and plen > best_len:
best_hop, best_len = next_hop, plen
return best_hop
# ---------- ডেটা লিংক লেয়ার (M3/L11 MAC/Ethernet, M3/L13 ARP, M3/L09 CRC এরর-ডিটেকশন) ----------
arp_table = {
"10.0.0.5": "AA:11:22:33:44:55", # প্রেরকের নিজের MAC
"192.168.1.42": "BB:66:77:88:99:00", # গন্তব্য সরাসরি-সংযুক্ত হওয়ায় ARP সরাসরি এর MAC রিজলভ করে
# (যদি গন্তব্য অন্য নেটওয়ার্কে হতো, ARP গেটওয়ের MAC রিজলভ করত -- L13)
}
def arp_resolve(ip, table):
"""ARP -- IP থেকে MAC অ্যাড্রেস রিজলভ করে (M3/L13)।"""
return table.get(ip, "UNKNOWN-MAC")
def crc_checksum(payload_repr):
"""M3/L09-এর mod-2 CRC নীতির একটি সরলীকৃত সংস্করণ -- প্রতিটি ক্যারেক্টারের ord-কে XOR করে একটি চেক-ভ্যালু তৈরি।"""
value = 0
for ch in payload_repr:
value ^= ord(ch)
return value
def encapsulate(segment, src_ip, dst_ip):
"""প্রেরক পাশ -- ট্রান্সপোর্ট -> নেটওয়ার্ক -> ডেটা লিংক, L01-এর প্যাটার্ন অনুসরণ করে।"""
next_hop = lookup_route(dst_ip, routing_table)
network_packet = {"src_ip": src_ip, "dst_ip": dst_ip, "next_hop": next_hop, "transport": segment}
payload_repr = repr(network_packet)
frame = {
"src_mac": arp_resolve(src_ip, arp_table),
"dst_mac": arp_resolve(dst_ip, arp_table),
"network_packet": network_packet,
"crc": crc_checksum(payload_repr),
}
return frame
def decapsulate(frame):
"""গ্রাহক পাশ -- ঠিক বিপরীত ক্রমে। CRC ব্যর্থ হলে ফ্রেম এখানেই বাতিল হয় (M3/L09) -- উপরের স্তর কিছুই জানবে না।"""
payload_repr = repr(frame["network_packet"])
if crc_checksum(payload_repr) != frame["crc"]:
return None
network_packet = frame["network_packet"]
return network_packet["transport"]
def reassemble(received_segments):
"""M5/L25 -- সিকোয়েন্স নম্বর অনুযায়ী সাজিয়ে আসল বাইট স্ট্রিম পুনর্গঠন (out-of-order আসলেও সঠিকভাবে কাজ করে)।"""
ordered = sorted(received_segments, key=lambda s: s["seq"])
return "".join(s["payload"] for s in ordered)
# ============================================================
# পুরো round-trip চালানো
# ============================================================
src_ip, dst_ip = "10.0.0.5", "192.168.1.42"
segments = segment_data(app_message)
print("=== প্রেরক পাশ -- এনক্যাপসুলেশন (ট্রান্সপোর্ট -> নেটওয়ার্ক -> ডেটা লিংক) ===")
frames = []
for seg in segments:
frame = encapsulate(seg, src_ip, dst_ip)
frames.append(frame)
print(f" সেগমেন্ট #{seg['seq']} '{seg['payload']}' -> ফ্রেম (dst_mac={frame['dst_mac']}, crc={frame['crc']})")
print("\n=== গ্রাহক পাশ -- ডিক্যাপসুলেশন (ডেটা লিংক -> নেটওয়ার্ক -> ট্রান্সপোর্ট) ও পুনর্গঠন ===")
received_segments = []
for frame in frames:
segment = decapsulate(frame)
if segment is None:
print(" একটি ফ্রেম CRC ব্যর্থতায় বাতিল হয়েছে -- এটি কখনো উপরের স্তরে পৌঁছায়নি!")
continue
received_segments.append(segment)
print(f" ফ্রেম থেকে উদ্ধার -> সেগমেন্ট #{segment['seq']}: '{segment['payload']}'")
final_message = reassemble(received_segments)
print("\n=== চূড়ান্ত যাচাই ===")
print("মূল বার্তা: ", app_message)
print("পুনর্গঠিত বার্তা: ", final_message)
assert final_message == app_message, "পূর্ণ স্ট্যাক round-trip ব্যর্থ হয়েছে!"
print("✓ সম্পূর্ণ স্ট্যাক (ট্রান্সপোর্ট -> নেটওয়ার্ক -> ডেটা লিংক -> এবং হুবহু বিপরীতে) নির্ভুলভাবে ডেটা পৌঁছে দিয়েছে।")
# ============================================================
# বাড়তি প্রদর্শনী -- একটি ফ্রেম করাপ্ট হলে কী হয়? (M3/L09 CRC-এর গুরুত্ব)
# ============================================================
print("\n=== বাড়তি প্রদর্শনী: একটি ফ্রেম ইচ্ছাকৃতভাবে করাপ্ট করা হলো ===")
corrupted_frame = dict(frames[0])
corrupted_frame["crc"] = corrupted_frame["crc"] ^ 0xFF # ইচ্ছাকৃতভাবে CRC ভুল করে দেওয়া
result = decapsulate(corrupted_frame)
print("করাপ্টেড ফ্রেম ডিক্যাপসুলেশনের ফলাফল:", result)
assert result is None, "করাপ্টেড ফ্রেম ভুলভাবে গৃহীত হয়েছে!"
print("✓ যেমন প্রত্যাশিত -- CRC ব্যর্থ হওয়ায় ডেটা লিংক লেয়ারই ফ্রেমটি বাতিল করেছে; নেটওয়ার্ক/ট্রান্সপোর্ট লেয়ার এটি কখনো দেখেইনি।")
arp_table-এ গন্তব্য IP (192.168.1.42) সরাসরি রিজলভ হয়েছে, কারণ
রাউটিং টেবিল দেখাচ্ছে এই গন্তব্য সরাসরি-সংযুক্ত (একই লোকাল নেটওয়ার্কে)। M3/L13-এ শেখা নিয়ম অনুযায়ী,
যদি গন্তব্যটি একটি ভিন্ন নেটওয়ার্কে হতো, ARP তখন ডেস্টিনেশনের নয়, বরং ডিফল্ট গেটওয়ের MAC
অ্যাড্রেস রিজলভ করত — প্রথম হপ পর্যন্তই ডেটা লিংক লেয়ারের দায়িত্ব, পুরো পথ পর্যন্ত নয়।
L01-এ আমরা শিখেছিলাম নেটওয়ার্কিং স্তরভিত্তিক কারণ প্রতিটি স্তর তার নিজের সংকীর্ণ দায়িত্ব পালন করে, অন্য স্তরের অভ্যন্তরীণ জটিলতা নিয়ে মাথা না ঘামিয়ে। এই ক্যাপস্টোন সিমুলেশন সেই একই নীতির একটি সম্পূর্ণ, কার্যকরী প্রমাণ — ট্রান্সপোর্ট লেয়ার সিকোয়েন্স নম্বর নিয়ে ব্যস্ত, নেটওয়ার্ক লেয়ার রাউটিং নিয়ে, ডেটা লিংক লেয়ার MAC ও এরর-ডিটেকশন নিয়ে — প্রতিটি স্তর তার কাজ ঠিকভাবে করলে, উপরে তোলা ডেটা ঠিক অবিকৃত অবস্থায় ফিরে আসে। এটিই ৫৭টি পাঠের সবচেয়ে গুরুত্বপূর্ণ, একক অন্তর্দৃষ্টি।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ উপরের সিমুলেশনে যদি M3/L09-এর CRC চেক ডেটা লিংক লেয়ারে ব্যর্থ হতো, তাহলে নেটওয়ার্ক বা ট্রান্সপোর্ট লেয়ার কি আদৌ এই ফ্রেমটি দেখতে পেত? এটি কেন গুরুত্বপূর্ণ?
না — decapsulate ফাংশনে দেখা যায়, CRC মিলছে না দেখলে ফাংশনটি সাথে সাথে None
রিটার্ন করে, network_packet-এর ভেতরে ঢুকেই দেখে না। অর্থাৎ একটি করাপ্ট ফ্রেম ডেটা লিংক
লেয়ারেই বাতিল হয়ে যায় — নেটওয়ার্ক লেয়ার বা ট্রান্সপোর্ট লেয়ার কখনোই জানতে পারে না যে এমন কোনো ফ্রেম
এসেছিল। এটি গুরুত্বপূর্ণ কারণ এর মানে প্রতিটি স্তরের এরর-হ্যান্ডলিং দায়িত্ব স্পষ্টভাবে বিভক্ত — উপরের
স্তরগুলোকে নিচের স্তরের ফিজিক্যাল/ট্রান্সমিশন এরর নিয়ে কখনো ভাবতেই হয় না, ঠিক L01-এ শেখা লেয়ারিং/মডুলারিটি
নীতির একটি সরাসরি, কার্যকরী প্রমাণ।
প্র ০২ এই সিমুলেশনে ট্রান্সপোর্ট লেয়ারের সিকোয়েন্স নম্বর (M5/L25) ঠিক কী সমস্যা সমাধান করছে, যা শুধু ডেটা লিংক লেয়ারের CRC (M3/L09) সমাধান করতে পারে না?
CRC শুধু একটি নির্দিষ্ট ফ্রেমের বিষয়বস্তু ট্রানজিটে বিকৃত হয়েছে কিনা তা যাচাই করে — এটি একাধিক ফ্রেমের মধ্যে ক্রম নিয়ে কিছুই জানে না। সিকোয়েন্স নম্বর একটি সম্পূর্ণ ভিন্ন সমস্যা সমাধান করে — একাধিক সেগমেন্ট বিভিন্ন পথে বা ভিন্ন সময়ে পৌঁছালেও (M5/L25-এ দেখা IP-এর কোনো অর্ডারিং গ্যারান্টি না থাকার কারণে) কীভাবে সেগুলো সঠিক ক্রমে পুনর্গঠন করা যায়। এই দুটি ভিন্ন স্তরের দুটি সম্পূর্ণ ভিন্ন, পরিপূরক দায়িত্ব — একটি বিষয়বস্তুর নির্ভুলতা নিশ্চিত করে, আরেকটি ক্রমের নির্ভুলতা।
প্র ০৩ এই সিমুলেশনের রাউটিং টেবিলে সরাসরি-সংযুক্ত গন্তব্যের জন্য ARP সরাসরি ডেস্টিনেশনের MAC রিজলভ করেছে। যদি গন্তব্যটি ভিন্ন একটি নেটওয়ার্কে হতো, তাহলে এই সিমুলেশনের কোন অংশ বদলাতে হতো?
lookup_route তখন destination-এর বদলে একটি next-hop রাউটার (যেমন ডিফল্ট গেটওয়ে) রিটার্ন
করত (উপরের রাউটিং টেবিলে "R1" বা "ডিফল্ট-গেটওয়ে" এন্ট্রির মতো), আর arp_resolve-কেও
ডেস্টিনেশন IP-এর বদলে সেই next-hop-এর MAC রিজলভ করতে হতো (M3/L13-এর মূল নিয়ম — ARP শুধু
পরবর্তী হপ পর্যন্তই কাজ করে, চূড়ান্ত গন্তব্য পর্যন্ত নয়)। ফ্রেমের dst_mac তখন গেটওয়ে
রাউটারের MAC হতো, প্যাকেটের ভেতরের dst_ip অবশ্য অপরিবর্তিতই থাকত — নেটওয়ার্ক লেয়ার ও
ডেটা লিংক লেয়ারের ঠিকানার এই বিভাজনই মাল্টি-হপ রাউটিংকে কাজ করতে দেয়।
অনুশীলন
-
পরীক্ষা করুন: উপরের কোড সেলে
app_message-এর মান পরিবর্তন করে অন্য কোনো বাংলা বা ইংরেজি বার্তা বসান এবং Run চেপে দেখুন সম্পূর্ণ round-trip এখনও সঠিকভাবে পাস করে কি না।যেকোনো বার্তার জন্যই শেষে "✓ সম্পূর্ণ স্ট্যাক ... নির্ভুলভাবে ডেটা পৌঁছে দিয়েছে" বার্তা আসা উচিত — কারণ
segment_data,encapsulate,decapsulateওreassembleকোনোটিই বার্তার নির্দিষ্ট বিষয়বস্তুর উপর নির্ভর করে না, শুধু তার দৈর্ঘ্য ও গঠনের উপর নির্ভর করে সাধারণভাবে কাজ করে। এটিই প্রমাণ করে সিমুলেশনটি একটি নির্দিষ্ট উদাহরণের জন্য হার্ডকোড করা নয়, বরং একটি প্রকৃত, সাধারণীকৃত এনক্যাপসুলেশন/ডিক্যাপসুলেশন প্রক্রিয়া। -
চিন্তা করুন: কোড সেলের শেষে
received_segments-এর ক্রম ইচ্ছাকৃতভাবে উল্টে দিয়ে (যেমনreversed(frames)দিয়ে লুপ চালিয়ে) দেখুনreassembleতবুও সঠিক ক্রমে বার্তা পুনর্গঠন করতে পারে কি না — কেন এটি সম্ভব?হ্যাঁ, তবুও সঠিক ফলাফল আসবে — কারণ
reassembleফাংশনটিsorted(..., key=lambda s: s["seq"])ব্যবহার করে সেগমেন্টগুলো তাদের সিকোয়েন্স নম্বর অনুযায়ী সাজায়, তারা কোন ক্রমে "এসেছে" তার উপর নির্ভর করে না। এটিই ঠিক M5/L25-এ শেখা TCP-এর মূল কৌশল — IP স্তর কোনো অর্ডারিং গ্যারান্টি না দিলেও (M4), সিকোয়েন্স নম্বরের কারণে ট্রান্সপোর্ট লেয়ার সবসময় সঠিক ক্রম পুনরুদ্ধার করতে পারে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পুরো কোর্সের সিলেবাস আবার দেখুন ৫৭টি পাঠ এই ৫৭টি পাঠ একসাথে ফিজিক্যাল সিগন্যাল থেকে অ্যাপ্লিকেশন প্রোটোকল পর্যন্ত সম্পূর্ণ প্রোটোকল স্ট্যাকটি কভার করেছে।
- Cloud Computing & DevOps কোর্স সঙ্গী কোর্স এই কোর্সে শেখা প্রোটোকল ভিত্তির উপর দাঁড়িয়ে ক্লাউডে VPC, লোড ব্যালেন্সার ও DNS ব্যবহারিকভাবে কীভাবে কনফিগার করা হয় তা শিখুন।
- Cybersecurity & Ethical Hacking কোর্স সঙ্গী কোর্স এই কোর্সে শেখা প্রোটোকলগুলোর দুর্বলতা কীভাবে বাস্তব আক্রমণ ও প্রতিরক্ষার ভিত্তি হয়ে ওঠে তা গভীরভাবে শিখুন।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps ও Computer Networks — সব এক জায়গায়।