Foundationপ্রথম নীতি থেকে
LEVEL 7লেসন ১০/২২মাঝারি৩৫ মিনিট

UDP — connectionless-এর সরলতা

UDP: User Datagram Protocol

8-byte header-এর সবচেয়ে ছোট transport protocol — কী ছেড়ে দিলে কী পাওয়া যায়, ones'-complement checksum-এর হাতে-কলমে গণিত, আর কোন application কেন এই অনিরাপদ দেখানো পথটাই বেছে নেয়।

এই লেসন শেষে আপনি পারবেন

  • UDP-র 8-byte header-এর প্রতিটা field — source port, destination port, length, checksum — একটা hexdump-এ দেখে চিনতে ও ব্যাখ্যা করতে পারবেন
  • UDP ঠিক কী কী ছেড়ে দেয় (ordering, reliability, flow/congestion control) আর কেন DNS, VoIP, game আর QUIC এই ছাড়গুলো ইচ্ছাকৃতভাবে মেনে নেয় — প্রতিটার যুক্তি আলাদা করে বলতে পারবেন
  • Datagram boundary মানে কী — কেন UDP-তে দুই send() দুই আলাদা message হয়ে পৌঁছায় অথচ TCP-তে মিশে byte-stream হয়ে যায় — তা উদাহরণ দিয়ে দেখাতে পারবেন
  • Ones'-complement checksum-এর গণিতটা 16-bit word ধরে ধরে হাতে করে করতে পারবেন, আর pseudo-header কেন যোগ হয় তা ব্যাখ্যা করতে পারবেন
  • Broadcast/multicast কেন TCP-তে অসম্ভব অথচ UDP-তে স্বাভাবিক — connection state-এর সাথে সম্পর্ক টেনে বুঝতে পারবেন

আগে যা বোঝা থাকা দরকার

আগে এটা বুঝি

TCP আমরা এখনো পড়িনি, কিন্তু সবাই তার নাম শুনেছি — নির্ভরযোগ্য, ক্রমবিন্যস্ত, সংযোগ-ভিত্তিক। তার ঠিক উল্টো চরিত্রের protocol-টাই আজ: UDP (User Datagram Protocol) — যেটা IP-র উপর বসে মাত্র 8 byte header যোগ করে, আর তারপর হাত গুটিয়ে বসে থাকে।

উপমাটা ডাকব্যবস্থার। TCP হলো টেলিফোন কল — আগে রিং, দুই পক্ষ “হ্যালো” বলে সংযোগ নিশ্চিত করে, তারপর কথা, শেষে বিদায়; কেউ কথা শুনতে ব্যর্থ হলে “একবার বলো আবার”। UDP হলো ডাকযোগে পাঠানো পোস্টকার্ড — ঠিকানা লিখে ফেলে দিলেন, পৌঁছাবে-না-পৌঁছাবে জানা নেই, দুইটা পোস্টকার্ড উল্টো ক্রমে আসতে পারে, কিন্তু প্রতিটা পোস্টকার্ড একটা সম্পূর্ণ, আলাদা বার্তা — পরেরটার সাথে মিশে না।

আজকের প্রশ্ন তিনটা: (১) এই 8 byte-এ ঠিক কী আছে? (২) TCP-র সুবিধাগুলো ছেড়ে দিয়ে কেউ কী পায়? (৩) সবচেয়ে ব্যস্ত কিছু application — DNS, VoIP, game, এমনকি আধুনিক internet-এর QUIC — কেন ইচ্ছাকৃতভাবে এই “ঝুঁকিপূর্ণ” পথ বেছে নেয়?

মূল ধারণা

8-byte header — পুরো protocol এক নিঃশ্বাসে

UDP datagram-এর কাঠামো: IP header-এর ঠিক পরে 8 byte, তারপর application-এর data। ব্যাস, এই যা।

ByteFieldকাজ
0–1Source Portফেরত ঠিকানা; দরকার না হলে 0
2–3Destination Portকোন application-কে deliver করা হবে
4–5Lengthheader+data মোট (ন্যূনতম 8)
6–7Checksumঅখণ্ডতা পরীক্ষা (IPv4-তে optional, IPv6-তে বাধ্যতামূলক)

IP ঠিকানা দেয় machine-পর্যন্ত delivery; port সেই delivery- কে machine-এর ভেতরের নির্দিষ্ট application-পর্যন্ত নিয়ে যায়। এই একটা ধারণাই UDP-র IP-র উপর সবচেয়ে বড় অবদান — বাকিটা প্রায় সব IP-রই সম্প্রসারণ।

যা ছেড়ে দেওয়া হলো — তালিকাটা মুখস্থ না, যুক্তিটা

TCP যা দেয়, UDP-র তালিকায় সেসবের জায়গায় শূন্য:

সুবিধাTCPUDPকার কাছে শূন্যটা ভালো
Delivery guaranteeহ্যাঁ, retransmit করেনা — lost = lostযেখানে পুরনো data নতুনের চেয়ে অমূল্য: live video
Orderingক্রমে সাজিয়ে দেয়আসার ক্রমেইযেখানে সর্বশেষ মানই একমাত্র সত্য: game state
Flow controlreceiver-কে চাপায় নানাযেখানে হারানো packet-এর চেয়ে বিলম্ব ভয়ংকর
Congestion controlnetwork-কে চাপায় নানাযেখানে app নিজেই গতি সামলায়
Connection setup1 RTT handshake0 RTTDNS-এর মতো এক request-এর জন্য handshake বাড়তি কর

লক্ষ্য করুন ডান কলামের প্যাটার্ন: যেখানে তাজা data পুরনো data- র চেয়ে মূল্যবান, অথবা যেখানে এক-একটা exchange ছোট — সেখানেই UDP জেতে।

কারা কেন UDP

  • DNS — এক প্রশ্ন, এক উত্তর, কয়েকশো byte। Handshake-এর খরচ উত্তরের চেয়ে বড় হয়ে যেত। (বড় উত্তরে TCP fallback — dns লেসনে বিস্তারিত)
  • VoIP / live video — 200 ms আগের audio frame আজ পুনরায় পাঠিয়েও কাজে লাগে না; হারালে ছেড়ে দাও, পরেরটা আসছে
  • Games — প্রতি টিকে সর্বশেষ position দরকার; পুরনো position retransmit করা অর্থহীন
  • DHCP — client-এর এখনো IP address নেই, কোনো connection সম্ভবই না; broadcast দরকার
  • QUIC — HTTP/3-র ভিত্তি: TCP-র reliability নিজের মতো userspace-এ পুনর্নির্মাণ করে, UDP-কে শুধু carrier বানায়

Datagram boundary — UDP-র সবচেয়ে অবমূল্যানুষীত গুণ

sendto() একবার ডাকলেন = এক datagram = receiver-এর recvfrom() একবারে ঠিক সেই পুরো বার্তা। Boundary অক্ষত।

TCP-তে কিন্তু না — সেখানে write() দুইবার করলেন 100 byte আর 200 byte, receiver-এর read(4096) একবারে 300 byte-ই ফেরত দিতে পারে, বা 50-50-200 ভাগে তিনবার — TCP মনেই রাখে না আপনার message কোথায় শেষ হয়েছিল, সে শুধু ক্রমবিন্যস্ত byte-stream। সেই কারণেই TCP-র উপর প্রতিটা protocol (HTTP সহ) নিজের framing নিজে আঁকে — length prefix, delimiter, ইত্যাদি।

UDP-তে এই সমস্যাই নেই: datagram আসে হয় পুরোটা, নয় একদমই না (ছোট datagram কখনো আধা-আধা আসে না — IP fragmentation হলেও reassembly-র পর receiver পুরো datagram-ই দেখে)।

ভেতরে কী ঘটছে

Checksum-এর গণিত — ones’ complement

UDP checksum 16-bit word-এর উপর ones’ complement arithmetic-এ হিসাব হয় — ঠিক সেই সংখ্যা-পদ্ধতি যেটা আমরা computer-representation module-এর signed-integers লেসনে two’s complement-এর আত্মীয় হিসেবে দেখেছি। নিয়ম:

  1. Data-কে 16-bit word-এ ভাগ করো (অজোড় byte হলে শেষে 0x00 জোড়ো)
  2. সব word জোড় করো — carry হলে সেটা আবার নিচে যোগ করো (end-around carry)
  3. যোগফলের ones’ complement নাও — সেটাই checksum

হাতে-কলমে একটা ছোট উদাহরণ — তিনটা word: 0x4500, 0x003c, 0xf7ff:

   4500
 + 003c
 ------
   453c        (carry নেই)
 + f7ff
 ------
  13d3b        17-bit হয়ে গেল → carry bit (1) ফিরিয়ে যোগ করো:
   3d3b
 +    1
 ------
   3d3c        checksum = ~0x3d3c = 0xC2C3

Receiver একই যোগফল checksum-সহ বার করলে পায় 0xFFFF — একটা bit-ও বদলালে 0xFFFF হবে না।

Pseudo-header: যোগফলের তালিকায় UDP header+data-র সাথে যোগ হয় আরও কিছু — IP-র থেকে নেওয়া source IP, destination IP, protocol (17) আর UDP length। এই ভুতুড়ে header wire-এ পাঠানো হয় না; শুধু হিসাবে ঢোকে। কেন? কারণ checksum তখন প্রমাণ করে দেয় datagram-টা সঠিক ঠিকানায়ই পৌঁছেছে — যদি NAT বা buggy router ভুল IP-তে পাঠিয়ে দেয়, receiver-এর checksum মিলবে না, datagram ফেলে দেওয়া হবে।

Broadcast আর multicast — TCP-র পৌঁছাতে না-পারা জায়গা

TCP connection মানেই দুইটা নির্দিষ্ট endpoint-এর মধ্যে state — তাই “সবাইকে একবারে বলো” TCP-তে সংজ্ঞাগতভাবেই অসম্ভব (হাজারটা আলাদা connection খুলে না হলে)। UDP-তে কোনো connection নেই বলে:

  • Broadcast — 255.255.255.255 বা subnet broadcast address- এ এক send, সবাই পায়। DHCP-র প্রথম ধাপ এভাবেই চলে: নতুন client- এর কারো address জানা নেই, তাই ঘরের সবাইকে চিৎকার করে জিজ্ঞেস করতেই হয়
  • Multicast — 224.0.0.0/4 রেঞ্জের group address; শুধু subscribe করা সদস্যরা পায়। IPTV, আর একই LAN-এর service discovery (mDNS) এখানে দৌড়ায়

এই দুটোই UDP-র একচেটিয়া ভূমি — এবং একই কারণে attack-এরও (amplification, dns লেসনে দেখবেন)।

উদাহরণ

একটা আসল UDP datagram হাতে খোলা

একটা DNS query-র UDP datagram-এর IP-header-পরবর্তী অংশের hexdump (dns লেসনের প্রস্তুতি হিসেবেও কাজে লাগবে):

c3 5f 00 35 00 2c 8a b2   <- UDP header (8 byte)

Field ধরে ধরে:

c3 5f   source port      = 0xc35f = 49999   (client-এর ephemeral port)
00 35   destination port = 53              (DNS)
00 2c   length           = 44 byte          (8 header + 36 DNS message)
8a b2   checksum         = 0x8ab2           (pseudo-header সহ হিসাব করা)

তার পরের 36 byte DNS message। লক্ষ্যণীয় তিনটা জিনিস:

  1. Header-এ কোনো seq, কোনো ack, কোনো flag নেই — কোনো connection-এর ধারণাই নেই; প্রতিটা datagram স্বয়ংসম্পূর্ণ
  2. Length field-টা সরাসরি বলে দিচ্ছে boundary কোথায় — receiver এক recvfrom()-এ ঠিক 44 byte-ই পাবে
  3. এই datagram হারালে DNS client নিজেই timeout মেরে আবার পাঠাবে — reliability-র দায়িত্ব এখানে application-এর, transport- এর নয়

Boundary বনাম stream — পাশাপাশি দুই জগৎ

একই দুইটা message পাঠলে দুই protocol-এ receiver যা দেখে:

sender: send("HELLO")  send("WORLD")

UDP receiver:                       TCP receiver:
  recvfrom() -> "HELLO"  (5 byte)     read() -> "HE"      (2 byte)
  recvfrom() -> "WORLD"  (5 byte)     read() -> "LLOWOR"  (6 byte)
                                      read() -> "LD"      (2 byte)
  দুইটা সম্পূর্ণ, আলাদা datagram      তিন read, ভাঙা টুকরো —
  boundary 100% সংরক্ষিত              boundary-র কোনো তথ্যই নেই
UDP প্রতিটা send-কে আলাদা ডিবি-তে রাখে; TCP সব গলিয়ে এক স্রোতে পরিণত করে — message-এর সীমানা রেখে যাওয়া application-এর দায়িত্ব।

ছবিতে

UDP-র স্তরে অবস্থান ও দায়িত্ব-বিভাজন

UDP = IP + port + checksum — এর বেশি কিছু না
  1. ApplicationDNS, DHCP, QUIC — reliability দরকার হলে নিজেই বানায়
  2. UDPports + optional checksum — boundary সংরক্ষণ, বাকি কিছু না
  3. IPaddressing, routing, fragmentation — best effort
  4. LinkEthernet/Wi-Fi — hop-to-hop CRC

আর একটা datagram-এর জীবন — কোথায় কী হারাতে পারে, কে জানে না:

app            UDP layer         IP layer          network
 |                |                 |                  |
 |-- sendto() --->|                 |                  |
 |                |-- datagram ---->|                  |
 |                |  (checksum)     |-- route -------->|
 |                |                 |                  |-- হারাল? শেষ।
 |                |                 |                  |-- পৌঁছাল?
 |                |                 |                  |    receiver UDP
 |                |                 |                  |    checksum মিলল?
 |  ...কিছুই জানা গেল না...          |                  |    না হলে ফেলে দেয়
UDP-তে হারানোর প্রতিটা সম্ভাবনা sender-এর কাছে নীরব — timeout ছাড়া কোনো সংবাদই ফেরে না; এটাই connectionless-এর দাম।

নিজে চালিয়ে দেখুন

EXPERIMENT

দুই terminal-এ UDP চ্যাট — boundary আর নীরবতা নিজের চোখে

macOS / Linux· ১০ মিনিট

ধাপ ১ — receiver চালু করুন প্রথম terminal-এ:

nc -u -l 9999        # macOS; Linux-এ কিছু build-এ: nc -u -l -p 9999

ধাপ ২ — sender দ্বিতীয় terminal-এ:

nc -u 127.0.0.1 9999

এখন sender-এ লিখুন hello এন্টার — receiver-এ সরাসরি ফুটবে। এরপর এক লাইনে অনেক শব্দ লিখুন, এন্টার দিন, তারপর আলাদা আলাদা ছোট ছোট লাইন কয়েকটা দ্রুত টাইপ করে পাঠান। Receiver-এ দেখবেন: প্রতিটা এন্টার = একটা সম্পূর্ণ আলাদা লাইন — কোনোটা মেশেনি, কোনোটা ভাগেনি। এটাই datagram boundary।

ধাপ ৩ — নীরব হারানো:

# receiver বন্ধ করে দিন (Ctrl-C), তারপর sender-এ আবার লিখুন:
# কিছুই হবে না — কোনো error, কোনো বার্তা নেই। UDP চুপ।

Sender-কে মনেই হবে সব ঠিক আছে। এটাই production-এ UDP app-দের application-level timeout/retry রাখার কারণ।

ধাপ ৪ (ঐচ্ছিক) — wire-এ নিজের চোখে দেখুন (tcpdump-এর জন্য sudo লাগে; শেষ করে Ctrl-C):

sudo tcpdump -i lo0 -X udp port 9999    # macOS loopback
# Linux-এ: sudo tcpdump -i lo -X udp port 9999

Output-এ UDP header-এর 8 byte আর তার পরে আপনার লেখা text-ই hex-এ দেখতে পাবেন — আজকের example section-এর হাতে-খোলা dump-এর সাথে মিলিয়ে পড়ুন।

loss নিয়ে সততার কথা: loopback interface-এ packet হারায় না — কার্নেলের ভেতরেই সব হয়। তাই “UDP-তে packet হারায়” দেখতে হলে বাস্তব network (Wi-Fi-তে দুই machine) দরকার, বা পরের build-এর মতো নিজেই drop সিমুলেট করতে হয়। এটাই localhost-এ UDP app টেস্ট করার সবচেয়ে বড় ফাঁদ।

এটা কী প্রমাণ করে

প্রতিটা send ঠিক এক recv হয়ে আসে (boundary সংরক্ষিত), বন্ধ receiver-এ পাঠালে কোনো error-ই আসে না (connection নেই), আর loopback-এ packet loss হয় না — তাই loss-এর বাস্তবতা simulation ছাড়া অনুভব করা যায় না।

নিজে বানান

BUILD IT

UDP time-server ও client — নিজের হাতে datagram design

Python · ●●○○○
  1. Server: UDP socket bind করে চিরকাল recvfrom করুন — কোনো accept, কোনো state নেই
  2. Request-এ sequence number রাখুন যাতে client চিনতে পারে কোন reply কোন প্রশ্নের
  3. Client: settimeout দিয়ে retry loop — হারালে আবার পাঠানো, এটাই app-level reliability
  4. Reply-তে server-এর টাইমস্ট্যাম্প ফেরত দিন — এক datagram-এই পুরো protocol
  5. Drop simulation যোগ করুন: প্রতি ৩-তম reply ইচ্ছাকৃতভাবে বাদ দিয়ে client-এর retry দেখুন

লক্ষ্য: দেখানো যে UDP-র উপর reliability বানানো সহজ — শুধু তা transport-এ না রেখে application-এ রাখতে হয়। পুরো protocol: client "TIME <seq>" পাঠায়, server "OK <seq> <unix-time>" ফেরত দেয়।

Server (time_server.py):

import socket, time, random

DROP_EVERY = 0          # 3 দিলে প্রতি ৩-তম reply ইচ্ছাকৃত drop (demo)

def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.bind(("0.0.0.0", 3777))
    print("UDP time-server on :3777 — কোনো connection, শুধু datagram")
    n = 0
    while True:
        data, addr = s.recvfrom(512)
        parts = data.decode(errors="replace").split()
        # খারাপ/অজানা request-ও এক datagram-এই এসেছে — ব্যর্থতা একাই সীমাবদ্ধ
        if len(parts) != 2 or parts[0] != "TIME":
            continue
        seq = parts[1]
        n += 1
        if DROP_EVERY and n % DROP_EVERY == 0:
            print(f"{seq}: ইচ্ছাকৃত drop (demo)")
            continue
        reply = f"OK {seq} {time.time():.6f}"
        s.sendto(reply.encode(), addr)

if __name__ == "__main__":
    main()

Client (time_client.py):

import socket, sys, time

def query(sock, seq, retries=3):
    msg = f"TIME {seq}".encode()
    for attempt in range(1, retries + 1):
        sock.sendto(msg, ("127.0.0.1", 3777))
        try:
            data, _ = sock.recvfrom(512)
            return data.decode(), attempt
        except socket.timeout:
            print(f"  seq={seq}: timeout, retry {attempt}/{retries}")
    return None, retries

def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.settimeout(0.7)
    for seq in range(1, 6):
        t0 = time.monotonic()
        reply, tries = query(s, seq)
        if reply is None:
            print(f"seq={seq}: FAILED — server কাছে নেই বা সব reply হারাল")
            continue
        rtt = (time.monotonic() - t0) * 1000
        print(f"seq={seq}: {reply}  (rtt={rtt:.1f} ms, tries={tries})")
        time.sleep(0.3)

if __name__ == "__main__":
    main()

চালান:

python3 time_server.py        # এক terminal-এ
python3 time_client.py        # আরেকটায়

এবার server-এ DROP_EVERY = 3 করে দুটোই আবার চালান — client- এর timeout-retry লাইনগুলো দেখবেন। প্রতিটা reply-তে নিজের পাঠানো seq ফিরে আসছে দেখে client নিশ্চিত হয় উত্তরটা তারই প্রশ্নের — এই ছোট ট্রিকটাই আজকের ICMP লেসনের identifier+sequence প্যাটার্নের প্রতিধ্বনি, এবং পরের লেসনের TCP seq/ack-র ভূতুড়ে পূর্বাভাস।

নিজে বাড়ান:

  1. Client-এ পরপর দ্রুত ৫টা request পাঠিয়ে reply গুলোর ক্রম উল্টে গেলে ধরুন (loopback-এ কষ্ট, কিন্তু delay যোগ করে সম্ভব)
  2. Server-এ প্রতি request-এ নতুন socket-এ reply দিয়ে দেখুন source port বদলালে client-এর কী হয়
  3. Broadcast যোগ করুন — SO_BROADCAST সেট করে 255.255.255.255-এ এক request, LAN-এর সব time-server একসাথে উত্তর দেবে

বাস্তব সিস্টেমে

বাস্তব জগতে UDP

QUIC / HTTP/3। আজ internet-এর ক্রমবর্ধমান অংশ QUIC-এর উপর চলে — যেটা TCP-র সব ভালো গুণ (reliability, congestion control, security) userspace-এ নিজে বানিয়ে UDP-র উপর বসিয়েছে। কেন? TCP kernel-এ বসে — এর বৈশিষ্ট্য বদলাতে OS upgrade লাগে; QUIC browser/app-এ বসে — প্রতি release-এ উন্নত হয়। আর UDP-র connectionless সরলতার কারণে middlebox-গুলো এতে হস্তক্ষেপ করতে পারে না।

VoIP আর WebRTC। ফোনকলে 300 ms পুরনো audio frame পুনর্জন্ম দেওয়ার কোনো অর্থ নেই — WebRTC তাই UDP-তে RTP চালায়, loss সামলায় codec-এর error concealment দিয়ে।

Games। প্রতি 16 ms-এ নতুন state — পুরনো state retransmit করলে খেলা আরও খারাপ হয়। তাই প্রায় সব real-time multiplayer game UDP: হারালে ছেড়ে দাও, client prediction দিয়ে ঢেকে দাও।

NTP। সময় সিঙ্কে এক request-এক reply — handshake-এর খরচই measurement-কে দূষিত করবে।

DNS amplification attack। UDP-র connectionless সরলতার অন্ধকার পিঠ: জাল source address দিয়ে ছোট request, বিশাল reply — victim-এর দিকে বন্দুক বানানো। TCP-তে handshake জাল address আটকে দেয়; UDP-তে কেউ আটকায় না। এজন্যই আজ open UDP resolver শিকার করা হয়।

DHCP। একটাই প্রোটোকল যেখানে UDP-র broadcast ক্ষমতা অপরিহার্য — IP-ই নেই এমন client-এর connection-এর প্রশ্নই ওঠে না।

যে ভুলগুলো সবাই করে

“UDP 'unreliable' মানে UDP-র data প্রায়ই হারায় — তাই খারাপ protocol।”

UDP কিছুই হারায় না — UDP কিছুই বাঁচায়ও না। Packet হারানোর কাজটা network করে, TCP আর UDP দুটোর ক্ষেত্রেই সমান হারে; পার্থক্য শুধু TCP সেটা লুকিয়ে retransmit করে, UDP সত্যি কথাটা বলে দেয়। “খারাপ” নয় — স্বচ্ছ। যে application হারানো সামলাতে জানে (live media, game), সে এই স্বচ্ছতা থেকেই কম latency পায়।

“UDP-তে congestion control-এর দরকার নেই, তাই আমার app-ও দেবে না।”

TCP-র congestion control internet-কে ১৯৮৮-এর collapse থেকে বাঁচায়েছিল — সেটা লেসন ১৩-এর গল্প। UDP-তে transport এটা দেয় না মানে দরকার নেই নয়; মানে দায়িত্ব application-এর। অসংযত UDP sender (বিশেষত video flood) একই path-এর TCP flow-গুলোকে চূর্ণ করে দেয় — এজন্যই RFC 8085 UDP application-দের congestion control মানার কথা বলে। QUIC ঠিক এই কারণেই নিজের congestion control বহন করে।

“UDP checksum সবসময় থাকে, মিললে data নিশ্চিত সঠিক।”

দুই ভাগে ভুল। এক: IPv4-তে checksum ঐচ্ছিক — 0x0000 লিখে পাঠানো datagram বৈধ। দুই: checksum যেকোনো corruption ধরে না — এটা 16-bit যোগফল, দুই bit উল্টে গেলে যোগফল আগেরটার সমানই হতে পারে। তীব্র অখণ্ডতা দরকার হলে application-level hash (যেমন আধুনিক storage/backup protocol-গুলোতে) দরকার।

“Connectionless মানে UDP-তে কোনো state-ই থাকে না।”

Transport layer-এ state নেই — ঠিক। কিন্তু বাস্তব ব্যবস্থায় state অন্যত্র জমে: NAT box প্রতিটা UDP flow-র mapping মনে রাখে (কিছু সময় পরে মুছে দেয় — দীর্ঘ UDP session-এর keepalive-এর কারণ), firewall rule তৈরি হয়, application নিজেই session/sequence রাখে। “Stateless” শব্দটা transport-এর সীমানার ভেতরেই সত্যি।

বুঝেছেন কি না দেখুন

1

UDP header-র চারটা field-এর নাম ও byte-আকার বলুন। কোনটা বাদ দিলেও UDP datagram এখনো valid থাকে?

স্মরণ

Source port (2), destination port (2), length (2), checksum (2)। Checksum বাদ দিলেও (IPv4-তে 0x0000 লিখে) datagram বৈধ; বাকি তিনটা অপরিহার্য — port ছাড়া delivery-র ঠিকানা নেই, length ছাড়া boundary জানা যায় না।

2

TCP-র উপর একটা protocol-এ sender write() করেছে 100 byte, তারপর সাথে সাথে আরও 200 byte। Receiver read(4096) একবার ডাকলে কত byte পেতে পারে? একই জিনিস UDP-তে করলে?

প্রয়োগ

TCP: যেকোনো কিছু 1 থেকে 300-এর মধ্যে — একবারেই 300 পেতে পারে (দুই write এক stream-এ মিশে), বা 100, বা একটু একটু করে; TCP message-এর সীমানা মনে রাখে না। UDP: প্রথম recvfrom-এ ঠিক 100, দ্বিতীয়টায় ঠিক 200 — boundary সংরক্ষিত, মিশে না।

3

DNS এক request-এক reply কেন TCP-তে না করে UDP-তে করে — তিনটা আলাদা কারণে ব্যাখ্যা করুন। কখন DNS তবু TCP ধরে?

যুক্তি

(১) Handshake মানে এক বাড়তি RTT — ছোট প্রশ্নের জন্য উত্তরের চেয়ে খরচ বড়; (২) ছোট প্রশ্নের জন্য state তৈরি করে server resource (connection table) নষ্ট — DNS server লাখো client সামলায়; (৩) Timeout-retry এক লাইনের কাজ, পুরো transport টানার দরকার নেই। TCP ধরে যখন উত্তর বড় — পুরনো 512 byte সীমা ছাড়ালে (truncation flag) বা DNSSEC-সই বড় response-এ — সেখানে reliability-র মূল্য সার্থক।

4

Ones’-complement যোগফল হলো 0x3D3C। Checksum field-এ কী লেখা হবে? Receiver পুরো যোগফল checksum-সহ বার করলে কী পাবে?

প্রয়োগ

Checksum = যোগফলের complement = ~0x3D3C = 0xC2C3। Receiver data+checksum সব যোগ করলে পাবে 0x3D3C + 0xC2C3 = 0xFFFF — ones’-complement-এ এটাই “শূন্য”। যেকোনো এক bit বদলালে যোগফল 0xFFFF থাকবে না, datagram ফেলে দেওয়া হবে।

5

একটা live score app-এ প্রতি সেকেন্ডে score update যায়। একটা update হারালে পরেরটা ১ সেকেন্ডেই আসে। এই app-এর জন্য TCP না UDP — কোনটা এবং কেন? যদি হঠাৎ নিয়ম হয় “একবার হারালেই ভুল score দেখাবে” — উত্তর কি বদলাবে?

ডিজাইন

প্রথম ক্ষেত্রে UDP — হারানো update পরেরটায় স্বয়ংক্রিয়ভাবে সংশোধিত হয়, তাই retransmit-এর মূল্য নেই; আর TCP-র হেড-অব-লাইন ব্লকিং (এক lost packet সব পরেরটাকে আটকে রাখে) live update-এ বিশেষ ক্ষতিকর। দ্বিতীয় ক্ষেত্রে হারানো update মানেই চিরতরে ভুল তথ্য — reliability অপরিহার্য — তখন TCP, অথবা UDP-র উপর নিজের ack+retransmit (অথবা প্রতি update-এ সম্পূর্ণ বর্তমান state পাঠানো — তাহলে আবার UDP-ই যথেষ্ট)। মূল শিক্ষা: protocol নয়, হারানো data-র মূল্য সিদ্ধান্ত ঠিক করে।

6

Pseudo-header wire-এ যায় না — তাহলে সেটা checksum-এ ঢুকিয়ে ঠিক কী লাভ? NAT box IP address বদলালে UDP checksum-এর কী হয়?

যুক্তি

লাভটা হলো misdelivery ধরা: checksum এখন IP-র ঠিকানার উপরও গণনা হয় — ভুল machine-এ পৌঁছালে (routing bug, NAT ভুল) receiver-এর হিসাব মিলবে না, datagram ফেলে যাবে — ভুল application-এ পৌঁছাবে না। NAT address বদলালে checksum আর বৈধ থাকে না — তাই NAT box নিজেই পুনর্গণনা করে (এজন্যই NAT UDP-র checksum field-কে ছুঁয়ে ফেলে; এটা NAT-এর প্রতিটা UDP datagram-এ করা বাধ্যতামূলক কাজ)।

এরপর কী

এরপর কী

আপনি এখন দেখেছেন connectionless-এর সরলতা — এবং তার দাম। পরের লেসনে আমরা উল্টো মেরুতে যাব: TCP।

পরের লেসনে:

  • তিন-ধাপের handshake — কেন ঠিক তিন ধাপ, দুই কেন নয় (একটা ৪০ বছরের পুরনো historical failure গল্পসহ)
  • 32-bit sequence number, flag bitfield, window — TCP segment header-র প্রতিটা field
  • পুরো state machine: LISTEN থেকে TIME_WAIT পর্যন্ত — আর কেন একটা connection বন্ধ হয়েও ৪ মিনিট মরে না

আজকের build-এ আপনি seq নিজের হাতে বানিয়েছিলেন — পরের লেসনে দেখবেন kernel সেই কাজটা কীভাবে প্রতিটা byte-এ করে।

আরও পড়ুন

  • RFC 768 — User Datagram Protocol — J. Postel (ISI) · UDP-র পুরো specification — মাত্র ৩ পাতা, ইতিহাসের সবচেয়ে ছোট গুরুত্বপূর্ণ RFC-গুলোর একটা
  • TCP/IP Illustrated, Volume 1: The Protocols — UDP chapter — W. Richard Stevens · UDP header, checksum গণনা আর wire-level উদাহরণের প্রামাণ্য উৎস
  • Computer Networking: A Top-Down Approach — Transport Layer — Kurose & Ross · UDP বনাম TCP-র তুলনামূলক কাঠামো আর checksum-এর গাণিতিক আলোচনা