সরাসরি পর্বে যান
পর্ব ৩৩ · ৬২-এর মধ্যে · মডিউল ৫

QUIC আর আধুনিক transport — UDP-র ওপর নতুন TCP

QUIC and Modern Transports — Handshake Latency, Head-of-Line Blocking, Streams, Connection IDs, Migration & RFC 9000

Taught in Bangla, this lesson explains why TCP is heavy for the modern web and how QUIC rebuilds a reliable transport over UDP in user space. It covers RFC 9000, 9001 and 9002, the TLS 1.3 handshake with 1-RTT and 0-RTT and replay risk, independent streams that remove head-of-line blocking, connection IDs and migration across networks.

এই পর্বে যা শিখবেন

  • TCP + TLS-এর handshake কত RTT খায়, multiplexed stream-এ head-of-line blocking কীভাবে ঘটে, ossification আর 4-tuple-বাঁধা সংযোগের সমস্যা কী
  • QUIC কেন UDP-র ওপর আর user space-এ, আর Google-এর পরীক্ষা থেকে IETF-এর RFC 9000 পর্যন্ত ইতিহাস
  • QUIC-এর 1-RTT আর 0-RTT handshake, packet-এর ধরন, এনক্রিপ্ট করা header, frame, variable-length integer আর stream ID
  • স্বাধীন stream, দুই স্তরের flow control, packet number আর ACK range, ack delay দিয়ে RTT হিসাব আর বদলযোগ্য congestion control
  • Connection ID, connection migration, path validation, anti-amplification আর Retry — সাথে বাস্তব প্রয়োগ, খরচ আর অন্য আধুনিক transport (MPTCP, SCTP, WebTransport)

১ · TCP চার দশকের পুরোনো ভরসা — আধুনিক ওয়েবে কোথায় আটকায়

আগের পাঁচটি পর্বে আপনি দেখেছেন TCP কত যত্ন করে বাইট পৌঁছে দেয়: three-way handshake (পর্ব ২৯), sequence আর ACK দিয়ে নির্ভরযোগ্যতা (পর্ব ৩০), receive window (পর্ব ৩১), আর congestion control (পর্ব ৩২)। TCP-র নকশা এসেছিল ১৯৮১ সালের RFC 793-এ, যখন “ওয়েবসাইট” শব্দটাই ছিল না। আজ একটি ওয়েবপাতা খুলতে ব্রাউজার একসাথে কয়েক ডজন ফাইল আনে, প্রায় সব ট্রাফিক এনক্রিপ্টেড, আর মানুষ হাঁটতে হাঁটতে Wi-Fi থেকে মোবাইল ডেটায় চলে যায়। এই নতুন বাস্তবতায় TCP-র চারটি দুর্বলতা স্পষ্ট হয়ে উঠেছে।

১. Handshake-এর দেরি

TCP সংযোগে ১ RTT, তার ওপর TLS 1.2-এ আরও ২ RTT (TLS 1.3-এ ১ RTT) — তারপরই প্রথম অনুরোধ যেতে পারে। দূরের সার্ভারে প্রতিটি RTT শত মিলিসেকেন্ড।

latency

২. Head-of-line blocking

HTTP/2 একটি TCP সংযোগে অনেক stream চালায়; কিন্তু TCP-র কাছে সব এক লম্বা বাইটের সারি। একটি segment হারালে তার পরের সব stream-এর ডেটা আটকে থাকে।

এক জ্যামে সবাই

৩. Ossification

Firewall, NAT আর অন্য middlebox TCP header পড়ে অভ্যস্ত; অচেনা option দেখলে ফেলে দেয় বা মুছে দেয়। আর TCP থাকে kernel-এ, যা বদলাতে বছর লাগে।

জমে যাওয়া

৪. ঠিকানায় বাঁধা সংযোগ

TCP সংযোগের পরিচয় 4-tuple: উৎস IP, উৎস port, গন্তব্য IP, গন্তব্য port. ফোন Wi-Fi ছেড়ে মোবাইল ডেটায় গেলে উৎস IP বদলায় — সংযোগ মারা যায়।

4-tuple

ঢাকা থেকে চট্টগ্রামে একটি কুরিয়ার সার্ভিস ভাবুন, যারা নিয়ম মানে: প্রাপক সব পার্সেল পাবেন ঠিক যে ক্রমে পাঠানো হয়েছে সেই ক্রমে। আপনি তিনটি আলাদা দোকানের মাল (জামা, বই, ওষুধ) একই ট্রাকে পাঠালেন। পথে জামার একটি বাক্স হারাল। নিয়ম অনুযায়ী গুদাম এখন বই আর ওষুধও আটকে রাখবে — যদিও সেগুলো পৌঁছে গেছে আর জামার সাথে তাদের কোনো সম্পর্ক নেই — যতক্ষণ না হারানো বাক্সের নতুন কপি আসে। এটাই TCP-র head-of-line blocking. QUIC-এর নিয়ম ভিন্ন: প্রতিটি দোকানের মাল নিজের ক্রমে, কিন্তু এক দোকানের জন্য অন্য দোকান অপেক্ষা করে না। আর কুরিয়ারের ট্র্যাকিং নম্বর যদি আপনার ঠিকানার বদলে পার্সেলের গায়েই লেখা থাকে, তাহলে আপনি বাসা বদলালেও পার্সেল আপনাকে খুঁজে পায় — এটাই connection ID.

Handshake-এর হিসাব — RTT গুনে গুনে

ব্রাউজার একটি নতুন HTTPS সার্ভারে প্রথম অনুরোধ পাঠাতে চায়। সময় গোনা যাক RTT-তে (একটি বার্তা যাওয়া আর উত্তর ফেরার সময়), প্রক্রিয়াকরণের সময় বাদ দিয়ে:

  • TCP + TLS 1.2: SYN → SYN-ACK (১ RTT); তারপর ClientHello → ServerHello…ServerHelloDone (১ RTT); তারপর ClientKeyExchange, Finished → Finished (আরও ১ RTT). মোট ৩ RTT পরে অনুরোধ যায়, উত্তরের প্রথম বাইট আসে ৪ RTT-তে।
  • TCP + TLS 1.3: TLS 1.3 (RFC 8446, ২০১৮) handshake ১ RTT-তে নামিয়েছে। মোট ২ RTT পরে অনুরোধ, প্রথম বাইট ৩ RTT-তে।
  • QUIC: transport আর TLS 1.3-এর handshake একসাথে, একই packet-এ। ১ RTT পরে অনুরোধ, প্রথম বাইট ২ RTT-তে। আগে এই সার্ভারে গিয়ে থাকলে 0-RTT: প্রথম packet-এই অনুরোধ, প্রথম বাইট ১ RTT-তে।

এক RTT কত বড়? আলো fiber-এ প্রতি মিলিসেকেন্ডে প্রায় ২০০ কিলোমিটার যায় (পর্ব ৫), আর তারের পথ সোজা নয়। ঢাকা থেকে সিঙ্গাপুরের কোনো সার্ভারে RTT ৬০ মিলিসেকেন্ডের মতো হতে পারে — পথ আর অপারেটরভেদে মান অনেক বদলায়, তাই এটি শুধু আনুমানিক উদাহরণ; ইউরোপ-আমেরিকায় আরও বেশি, ভূস্থির satellite-এ কয়েকশো মিলিসেকেন্ড। নিচের ক্যালকুলেটর কয়েকটি RTT-তে প্রথম বাইট পর্যন্ত সময় হিসাব করে।

// Time until the first byte of an HTTP response reaches the client, counted in RTTs.
// setup = round trips spent before the request can be sent; +1 RTT for request/response.
// (Ignores transmission time, server think-time, packet loss and DNS.)
const stacks = [
  { name: "TCP + TLS 1.2",            setup: 1 + 2 },
  { name: "TCP + TLS 1.3",            setup: 1 + 1 },
  { name: "TCP + TLS 1.3 0-RTT (resume)", setup: 1 + 0 },
  { name: "QUIC 1-RTT (new)",         setup: 1 },
  { name: "QUIC 0-RTT (resume)",      setup: 0 },
];
const rtts = [
  { label: "20 ms (same city)", ms: 20 },
  { label: "60 ms (~Dhaka-Singapore, approx.)", ms: 60 },
  { label: "80 ms", ms: 80 },
  { label: "250 ms (far / satellite-like)", ms: 250 },
];
console.log("stack".padEnd(30) + "RTTs  " + rtts.map(r => (r.ms + "ms").padStart(7)).join(""));
for (const s of stacks) {
  const n = s.setup + 1;
  console.log(s.name.padEnd(30) + String(n).padEnd(6) + rtts.map(r => String(n * r.ms).padStart(7)).join(""));
}
const old = stacks[0].setup + 1, quic = stacks[3].setup + 1;   // 4 RTT vs 2 RTT
for (const r of rtts)
  console.log(`${r.label}: QUIC 1-RTT saves ${(old - quic) * r.ms} ms vs TCP+TLS 1.2 on a new connection`);

আউটপুট:

stack                         RTTs     20ms   60ms   80ms  250ms
TCP + TLS 1.2                 4          80    240    320   1000
TCP + TLS 1.3                 3          60    180    240    750
TCP + TLS 1.3 0-RTT (resume)  2          40    120    160    500
QUIC 1-RTT (new)              2          40    120    160    500
QUIC 0-RTT (resume)           1          20     60     80    250
20 ms (same city): QUIC 1-RTT saves 40 ms vs TCP+TLS 1.2 on a new connection
60 ms (~Dhaka-Singapore, approx.): QUIC 1-RTT saves 120 ms vs TCP+TLS 1.2 on a new connection
80 ms: QUIC 1-RTT saves 160 ms vs TCP+TLS 1.2 on a new connection
250 ms (far / satellite-like): QUIC 1-RTT saves 500 ms vs TCP+TLS 1.2 on a new connection

প্রতিটি সারির সূত্র: প্রথম বাইটের সময় = (setup RTT + ১) × RTT. TCP + TLS 1.2-এ ৪ RTT — ২৫০ মিলিসেকেন্ডের পথে প্রথম বাইট আসতে পুরো এক সেকেন্ড, যার ৭৫০ মিলিসেকেন্ড শুধু প্রস্তুতিতে! QUIC-এর নতুন সংযোগ ২ RTT-তে নামায়: ৬০ মিলিসেকেন্ডের পথে ১২০ মিলিসেকেন্ড বাঁচে। লক্ষ করুন, “TCP + TLS 1.3 0-RTT” আর “QUIC 1-RTT” দুটোই ২ RTT — TCP-র নিজের handshake-এর ১ RTT কোনো TLS কৌশলে কমে না। দূরত্ব যত বেশি, QUIC-এর লাভ তত বড়।

4-tuple — যে ঠিকানায় সংযোগ বাঁধা

TCP সংযোগ চেনা হয় 5-tuple-এর চারটি মান দিয়ে (protocol তো TCP-ই): উৎস IP, উৎস port, গন্তব্য IP, গন্তব্য port. এর যেকোনো একটি বদলালে সার্ভার আর সেই সংযোগ খুঁজে পায় না — Wi-Fi ছেড়ে মোবাইল ডেটায় গেলে, বা নীরবতার পর NAT নতুন বাইরের port দিলে (NAT rebinding, পর্ব ১৯)। তখন অ্যাপকে নতুন handshake দিয়ে আবার শুরু করতে হয়।

২ · Head-of-line blocking — একটি হারানো segment সব stream থামায়

HTTP/1.1-এ ব্রাউজার একটি সার্ভারে ৬টির মতো আলাদা TCP সংযোগ খুলত। HTTP/2 (২০১৫) একটি TCP সংযোগের ভেতরে অনেকগুলো stream চালায় — প্রতিটি অনুরোধ একটি stream, ডেটা ছোট frame-এ ভাগ হয়ে পালাক্রমে যায়; এটাই multiplexing. কম সংযোগ, কম handshake — কিন্তু একটি গোপন সমস্যা থেকে গেল।

TCP জানে না ভেতরে কতগুলো stream আছে। তার কাছে সবকিছু একটি একক বাইটের সারি (byte stream), আর তার প্রতিশ্রুতি: অ্যাপ বাইট পাবে ঠিক ক্রমে। ধরুন সারির ১০০১ থেকে ২৪৪০ নম্বর বাইটের segment-টি হারিয়েছে, কিন্তু ২৪৪১ থেকে পরের সব segment পৌঁছে গেছে। Kernel সেগুলো buffer-এ রেখে দেবে, অ্যাপকে দেবে না — কারণ মাঝখানে ফাঁক। ফাঁকটি হয়তো stream A-র, আর পরের ডেটা stream B আর C-র, যাদের কোনো দোষ নেই। এই অবস্থার নাম transport-level head-of-line blocking. (Router-এর input queue-তে সামনের packet আটকে পেছনেরগুলো থামানোকেও head-of-line blocking বলে — পর্ব ১৫; ধারণা একই, জায়গা আলাদা।)

একটি উদাহরণ সংখ্যা দিয়ে

তিনটি stream (A, B, C), প্রতিটিতে ৩টি packet, পালাক্রমে পাঠানো: A1 B1 C1 A2 B2 C2 A3 B3 C3. প্রতি ১০ মিলিসেকেন্ডে একটি packet যায়, একদিকের delay ৪০ মিলিসেকেন্ড (RTT ৮০)। A2 পথে হারায়; প্রেরক হারানো ডেটা আবার পাঠায়, আর সরলতার জন্য ধরি সেটি মূল সময়ের ঠিক এক RTT (৮০ মিলিসেকেন্ড) পরে পৌঁছায়। বাস্তবে এই দেরি নির্ভর করে কত দ্রুত ক্ষতি ধরা পড়ে তার ওপর (পর্ব ৩০-এর fast retransmit আর timeout).

  1. প্রেরকA1 B1 C1 A2 …প্রতি ১০ ms
  2. পথA2 হারালt = 70 ms
  3. প্রাপকের bufferB2 C2 A3 B3 C3পৌঁছেছে
  4. A2-র ডেটা আবারt = 150 msফাঁক পূরণ
প্রাপকের অ্যাপ কী পেয়েছে (TCP বনাম QUIC)
সময়
ঘটনা
TCP: অ্যাপ পেয়েছে
QUIC stream A
QUIC stream B
QUIC stream C
  1. প্রথম তিনটি packet ঠিকঠাক। A1 পৌঁছাল ৪০, B1 ৫০, C1 ৬০ মিলিসেকেন্ডে। TCP-তে এগুলো সারির প্রথম তিনটি টুকরো, ক্রম ঠিক — অ্যাপ সঙ্গে সঙ্গে পেল। QUIC-এ প্রতিটি নিজের stream-এ গেল।
  2. A2 হারাল। ৭০ মিলিসেকেন্ডে A2 আসার কথা ছিল, আসেনি। প্রাপক এখনো জানে না এটি হারিয়েছে নাকি দেরিতে আসবে। দুই protocol-এর অ্যাপই এখন পর্যন্ত একই জিনিস পেয়েছে।
  3. পার্থক্য শুরু: B2 আর C2 এল। TCP-র চোখে এগুলোর বাইট নম্বর A2-র পরে, মাঝখানে ফাঁক — তাই kernel buffer-এ রেখে দিল, অ্যাপ কিছু পেল না। QUIC জানে B2 stream B-র, আর stream B-তে কোনো ফাঁক নেই — তাই ৮০ মিলিসেকেন্ডেই B2 আর ৯০-এ C2 অ্যাপের হাতে।
  4. A3, B3, C3 এল। TCP-তে এখন পাঁচটি segment buffer-এ আটকা, অ্যাপ এখনো শুধু A1 B1 C1 পেয়েছে। QUIC-এ stream B ১১০ আর C ১২০ মিলিসেকেন্ডে সম্পূর্ণ। শুধু A3 অপেক্ষা করছে, কারণ stream A-র নিজের ভেতরেই A2-র ফাঁক।
  5. হারানো ডেটা ফিরে এল ১৫০ মিলিসেকেন্ডে। TCP-তে ফাঁক পূরণ হতেই আটকে থাকা সব ডেটা একসাথে অ্যাপে — তিনটি stream-ই শেষ হলো ১৫০-এ। QUIC-এ শুধু stream A শেষ হলো ১৫০-এ; B আর C তার ৪০ আর ৩০ মিলিসেকেন্ড আগেই শেষ। লক্ষ করুন, QUIC হারানো packet আবার পাঠায় না — হারানো frame-এর ডেটা নতুন নম্বরের packet (9)-এ যায় (অংশ ৬)।

এবার একই হিসাব একটি simulator-এ। প্রতিটি packet-এর পৌঁছানোর সময় থেকে দুটি নিয়মে হিসাব হয়: TCP-তে একটি packet অ্যাপে যায় তার নিজের পৌঁছানো আর আগের সব packet-এর অ্যাপে যাওয়ার মধ্যে যেটি পরে; QUIC-এ শুধু একই stream-এর আগের packet-এর সাথে তুলনা।

// 3 streams (A, B, C) share one connection; packets are interleaved A1 B1 C1 A2 ...
// One packet is sent every 10 ms, one-way delay 40 ms, RTT 80 ms. Packet A2 is lost;
// its data is resent and arrives one RTT (80 ms) later than the original would have.
const order = ["A1","B1","C1","A2","B2","C2","A3","B3","C3"], LOST = "A2", RTT = 80;
const arrive = {};
order.forEach((p, i) => arrive[p] = 40 + 10 * i + (p === LOST ? RTT : 0));

// (a) TCP: one byte stream. Nothing after a gap reaches the app until the gap is filled.
const tcp = {}; let t = 0;
for (const p of order) { t = Math.max(t, arrive[p]); tcp[p] = t; }

// (b) QUIC: in-order delivery only WITHIN each stream; streams do not wait for each other.
const quic = {}, last = {};
for (const p of order) {
  const s = p[0];
  quic[p] = Math.max(last[s] || 0, arrive[p]);
  last[s] = quic[p];
}
console.log("pkt  arrives  TCP->app  QUIC->app   (ms)");
for (const p of order)
  console.log(p.padEnd(5) + String(arrive[p]).padEnd(9) + String(tcp[p]).padEnd(10) +
              String(quic[p]) + (p === LOST ? "   <- lost once, resent" : ""));
for (const s of "ABC") {
  const done = (m) => Math.max(...order.filter(p => p[0] === s).map(p => m[p]));
  console.log(`stream ${s} complete:  TCP ${done(tcp)} ms   QUIC ${done(quic)} ms`);
}

আউটপুট:

pkt  arrives  TCP->app  QUIC->app   (ms)
A1   40       40        40
B1   50       50        50
C1   60       60        60
A2   150      150       150   <- lost once, resent
B2   80       150       80
C2   90       150       90
A3   100      150       150
B3   110      150       110
C3   120      150       120
stream A complete:  TCP 150 ms   QUIC 150 ms
stream B complete:  TCP 150 ms   QUIC 110 ms
stream C complete:  TCP 150 ms   QUIC 120 ms

“TCP->app” কলামে A2-র পরের সব packet ১৫০-এ আটকে আছে — B2 এসেছিল ৮০-তে, তবু অ্যাপ পেল ৭০ মিলিসেকেন্ড পরে। “QUIC->app” কলামে B আর C-র packet যে মুহূর্তে এসেছে সেই মুহূর্তেই অ্যাপে গেছে; শুধু A3 (১০০-তে এসে) ১৫০ পর্যন্ত অপেক্ষা করেছে, কারণ সে A2-র পরের অংশ। শেষ তিন লাইন সারাংশ: stream B আর C TCP-তে ১৫০-এ, QUIC-এ ১১০ আর ১২০-এ শেষ। A যদি একটি বড় ছবি আর B একটি CSS ফাইল হয়, তাহলে ছবির একটি packet হারানোয় পাতার নকশা দেরিতে আসে — এই অহেতুক দেরি QUIC দূর করে। Packet loss যত বেশি (দুর্বল মোবাইল সংকেত, ব্যস্ত Wi-Fi), পার্থক্য তত বড়।

QUIC সব head-of-line blocking দূর করে না। একটি stream-এর ভেতরে ক্রম এখনো রক্ষা হয় — উপরের A3 যেমন A2-র জন্য অপেক্ষা করল। আর অ্যাপ নিজে যদি এক stream-এর ডেটার জন্য অন্যটি থামিয়ে রাখে, transport কিছু করতে পারে না। HTTP/3-কে এজন্য header compression নতুন করে নকশা করতে হয়েছে (QPACK, পর্ব ৩৭), কারণ HTTP/2-এর HPACK stream-গুলোর মাঝে ক্রম ধরে নিত।

৩ · কেন UDP-র ওপর, কেন user space-এ — আর QUIC-এর ইতিহাস

TCP-র সমস্যাগুলো তো জানা। তাহলে TCP-কে সারিয়ে নিলেই হতো? চেষ্টা হয়েছিল — আর সেই চেষ্টার ব্যর্থতাই QUIC-এর জন্মের কারণ।

Ossification: ইন্টারনেট জমে গেছে

পর্ব ০৩-এ ossification আর end-to-end principle-এর কথা পড়েছেন। মূল কথা: নীতিগতভাবে router শুধু IP header দেখার কথা, কিন্তু বাস্তবে পথের অনেক যন্ত্র — firewall, NAT, load balancer, “ত্বরণকারী” proxy — TCP header খুলে পড়ে, কখনো বদলায়। এগুলো বানানো হয়েছিল সেই সময়ের TCP দেখে। তাই কেউ নতুন option পাঠালে অনেক যন্ত্র সেটিকে সন্দেহজনক ভেবে মুছে দেয়, বা পুরো packet ফেলে দেয়। ২০১১ সালের একটি পরিচিত পরিমাপ-গবেষণা দেখিয়েছিল যে ইন্টারনেটের উল্লেখযোগ্য সংখ্যক পথে অচেনা TCP option বা অস্বাভাবিক sequence number টিকে থাকে না।

উদাহরণ TCP Fast Open (RFC 7413, ২০১৪): প্রথম SYN-এর সাথেই ডেটা পাঠিয়ে handshake-এর এক RTT বাঁচানোর কৌশল। দুই প্রান্তের OS সমর্থন করলেও পথের middlebox অনেক সময় ডেটাসহ SYN ফেলে দিত, ফলে ব্রাউজারগুলোকে জটিল fallback রাখতে হতো, আর শেষ পর্যন্ত সাধারণ ওয়েবে এটি ব্যাপক হয়নি। Multipath TCP-কেও middlebox-এর সাথে খাপ খাওয়াতে নকশায় অনেক কৌশল যোগ করতে হয়েছে।

Kernel-এর ধীর চাকা

দ্বিতীয় বাধা: TCP থাকে OS-এর kernel-এ। নতুন TCP আচরণ পৌঁছাতে লাগে নতুন kernel — সার্ভারে হয়তো কয়েক মাসে, কিন্তু কোটি কোটি ফোনে, বিশেষত যেগুলোর নির্মাতা আর হালনাগাদ দেয় না, বছরের পর বছর পুরোনো kernel চলে। অথচ ব্রাউজার কয়েক সপ্তাহ পরপর হালনাগাদ হয়। Protocol অ্যাপের ভেতরে, অর্থাৎ user space-এ থাকলে নতুন সংস্করণ পৌঁছায় অ্যাপের হালনাগাদের সাথে।

তাহলে কেন UDP?

একটি সম্পূর্ণ নতুন transport protocol (IP header-এ নতুন protocol নম্বর) বানালে কী হতো? SCTP-এর অভিজ্ঞতা উত্তর দেয়: ২০০০ সাল থেকে মান হয়েও বাসার NAT আর firewall পেরোতে পারে না, কারণ সেগুলো শুধু TCP আর UDP চেনে (পর্ব ১৯)। UDP প্রায় সব জায়গায় চলে — DNS, ভিডিও কল, গেম সবাই UDP ব্যবহার করে। আর UDP প্রায় কিছুই করে না (পর্ব ২৮): শুধু port আর একটি checksum, ৮ বাইটের header. এটি একটি ফাঁকা খাম, যার ভেতরে QUIC নিজের সব নিয়ম লিখতে পারে। তাই QUIC:

  • UDP datagram-এর ভেতরে চলে (সাধারণত সার্ভারের UDP port 443) — NAT আর firewall যা চেনে তা-ই দেখে।
  • নিজেই নির্ভরযোগ্যতা, ক্রম, flow control আর congestion control করে — যা TCP kernel-এ করে।
  • নিজের header-এর প্রায় সবটা এনক্রিপ্ট করে, যাতে middlebox ভবিষ্যতে আবার “অভ্যস্ত” হয়ে QUIC-কেও জমিয়ে দিতে না পারে।

Google-এ Jim Roskind-এর নেতৃত্বে ২০১২ সালের দিকে QUIC-এর নকশা শুরু হয়; ২০১৩ সালে Google প্রকাশ্যে জানায় আর Chrome-এ পরীক্ষামূলকভাবে চালু করে। নামটি শুরুতে “Quick UDP Internet Connections”-এর সংক্ষেপ হিসেবে পরিচিত ছিল, কিন্তু IETF-এর মানে QUIC আর কোনো সংক্ষেপ নয় — শুধু একটি নাম। Google-এর সংস্করণ (এখন “gQUIC” বলা হয়) নিজস্ব এনক্রিপশন (“QUIC Crypto”) ব্যবহার করত। ২০১৬ সালে IETF-এ QUIC working group গঠিত হয়, এনক্রিপশন বদলে TLS 1.3 নেওয়া হয়, আর প্রায় পাঁচ বছরের কাজ শেষে মে ২০২১-এ প্রকাশিত হয় RFC 9000 (মূল transport), RFC 9001 (QUIC-এ TLS ব্যবহার), RFC 9002 (loss detection ও congestion control) আর RFC 8999 (সংস্করণ-নিরপেক্ষ বৈশিষ্ট্য)। এর ওপর HTTP/3 আসে RFC 9114-এ, জুন ২০২২ সালে।

মূল ধারণা

QUIC আসলে “UDP-র ওপর TCP + TLS + HTTP/2-এর stream” — একটি স্তরে মিশিয়ে। UDP এখানে শুধু NAT আর firewall পেরোনোর বাহন; নির্ভরযোগ্যতা থেকে শুরু করে এনক্রিপশন পর্যন্ত সব দায়িত্ব QUIC-এর। তাই “QUIC UDP-তে চলে, তাই অনির্ভরযোগ্য” — এই কথা ভুল।

৪ · QUIC handshake — TLS 1.3 ভেতরেই, 1-RTT আর 0-RTT

TCP + TLS-এ দুটি আলাদা handshake পরপর চলে: আগে TCP সংযোগ, তারপর তার ওপর TLS. QUIC-এর মূল কৌশল হলো এদের একসাথে চালানো। TLS 1.3-এর বার্তাগুলো (ClientHello, ServerHello, Certificate, Finished…) QUIC-এর CRYPTO frame-এ চড়ে যায়, আর সেই একই packet-গুলোতে QUIC নিজের transport-এর তথ্য (connection ID, flow control-এর সীমা ইত্যাদি, যাদের বলে transport parameter) বিনিময় করে। TLS এখানে record স্তর ব্যবহার করে না; শুধু handshake চালিয়ে key তৈরি করে, আর সেই key দিয়ে QUIC নিজেই packet এনক্রিপ্ট করে (RFC 9001). TLS 1.3-এর বিস্তারিত পর্ব ৪৮-এ।

  1. ক্লায়েন্টt = 0প্রথম packet
  2. সার্ভারt = ½ RTTউত্তর
  3. ক্লায়েন্টt = 1 RTTQUIC: অনুরোধ যায়
  4. প্রথম বাইটQUIC 2 │ TCP 3 RTTউত্তর এল
একই মুহূর্তে দুই পথে কী ঘটছে
সময়
TCP + TLS 1.3
QUIC
key-এর অবস্থা
  1. প্রথম packet. TCP পাঠায় একটি খালি SYN — শুধু “কথা বলতে চাই”. QUIC একবারেই পাঠায় TLS-এর ClientHello (যাতে ক্লায়েন্টের key share আছে) আর নিজের transport parameter, একটি Initial packet-এ; datagram PADDING দিয়ে অন্তত ১২০০ বাইট করা হয় (অংশ ৮-এ কারণ)। Initial packet-ও এনক্রিপ্টেড, তবে তার key তৈরি হয় ক্লায়েন্টের বেছে নেওয়া Destination Connection ID আর একটি প্রকাশ্য salt থেকে — তাই পথের যেকেউ এটি খুলতে পারে; এটি গোপনীয়তা নয়, অন্যের packet হুবহু বানানো আর ভুলবশত বদলানো ঠেকানোর ব্যবস্থা।
  2. সার্ভারের উত্তর। TCP-তে শুধু SYN-ACK. QUIC-এ সার্ভার একবারেই পাঠায় ServerHello (Initial packet-এ) আর certificate, CertificateVerify, Finished (Handshake packet-এ, handshake key দিয়ে এনক্রিপ্টেড)। চাইলে সার্ভার এখনই 1-RTT key দিয়ে কিছু ডেটাও পাঠাতে পারে।
  3. এক RTT শেষ — পার্থক্য স্পষ্ট। TCP সবে সংযোগ পেল, এখন TLS-এর ClientHello পাঠাচ্ছে। QUIC ক্লায়েন্ট সার্ভারের certificate যাচাই করে নিজের Finished পাঠায়, আর একই datagram-এ 1-RTT packet-এ প্রথম HTTP অনুরোধ — stream 0-তে GET /.
  4. দেড় RTT. TCP-র দিকে সার্ভার এখন TLS-এর ServerHello থেকে Finished পর্যন্ত পাঠাচ্ছে। QUIC-এর দিকে সার্ভার অনুরোধ পেয়ে উত্তর পাঠাতে শুরু করেছে, সাথে HANDSHAKE_DONE frame — ক্লায়েন্ট এতে নিশ্চিত হয় যে handshake দুই দিকেই সম্পন্ন।
  5. দুই RTT — QUIC-এ উত্তর হাতে। QUIC ক্লায়েন্ট উত্তরের প্রথম বাইট পেল। TCP ক্লায়েন্ট কেবল নিজের Finished আর অনুরোধ পাঠাচ্ছে।
  6. তিন RTT — TCP-তে উত্তর এল। ততক্ষণে QUIC পুরো এক RTT ধরে ডেটা পাচ্ছে। অংশ ১-এর ক্যালকুলেটরের সাথে মিলিয়ে দেখুন: TCP + TLS 1.3-এ ৩ RTT, QUIC-এ ২. TLS 1.2 হলে TCP-র দিকে আরও এক RTT যোগ হতো।

0-RTT — আগের পরিচয়ে প্রথম packet-এই অনুরোধ

আগের সংযোগ শেষে সার্ভার ক্লায়েন্টকে একটি session ticket দিয়ে রাখতে পারে (TLS 1.3-এর resumption). পরের বার ক্লায়েন্ট সেই ticket থেকে পাওয়া key দিয়ে প্রথম datagram-এই 0-RTT packet-এ অনুরোধ পাঠাতে পারে — সার্ভারের কোনো উত্তরের অপেক্ষা ছাড়াই। ক্যালকুলেটরে দেখেছেন: প্রথম বাইট ১ RTT-তে।

0-RTT-এর দাম: replay. 0-RTT ডেটা সার্ভারের নতুন কোনো অবদান ছাড়াই এনক্রিপ্ট হয়, তাই পথের কেউ সেই packet রেকর্ড করে পরে হুবহু আবার পাঠালে সার্ভার সেটিকে বৈধ ভাবতে পারে — একে বলে replay attack. একটি পাতা দেখার অনুরোধ (GET) দুবার ঘটলে ক্ষতি নেই; কিন্তু ১,০০০ টাকা পাঠানোর অনুরোধ দুবার ঘটলে বিপদ। প্রতিরক্ষা: সার্ভার শুধু idempotent অনুরোধ 0-RTT-তে গ্রহণ করে, ticket একবারই ব্যবহারযোগ্য রাখে বা সময়সীমা দেয়, আর HTTP-তে সন্দেহ হলে “425 Too Early” উত্তর দিয়ে ক্লায়েন্টকে handshake শেষে আবার পাঠাতে বলে (RFC 8470). লেনদেন, লগইন বা যেকোনো অবস্থা-বদলানো অনুরোধ 0-RTT-তে না পাঠানোই নিয়ম।

Packet-এর চার ধরন আর তিনটি নম্বর-ভান্ডার

PacketHeaderকোন keyকী বহন করে
Initiallong, type 0x0DCID থেকে তৈরি Initial keyClientHello / ServerHello (CRYPTO), ACK, PADDING; ক্লায়েন্টের Initial-এ token থাকতে পারে
0-RTTlong, type 0x1আগের session থেকে পাওয়া keyHandshake শেষের আগেই ক্লায়েন্টের অ্যাপ-ডেটা (replay-ঝুঁকি)
Handshakelong, type 0x2handshake keyCertificate, Finished ইত্যাদি (CRYPTO), ACK
Retrylong, type 0x3—সার্ভারের দেওয়া token — “এটা নিয়ে আবার আসুন” (অংশ ৮)
1-RTTshort1-RTT keyবাকি সংযোগের সব ডেটা: STREAM, ACK, MAX_DATA…

Packet number রাখা হয় তিনটি আলাদা ভান্ডারে (packet number space): Initial, Handshake আর application data (0-RTT ও 1-RTT একসাথে); প্রতিটিতে নম্বর শূন্য থেকে শুধু বাড়ে, ACK-ও আলাদা। এ ছাড়া Version Negotiation packet সমর্থিত সংস্করণের তালিকা জানায়; QUIC version 1-এর নম্বর 0x00000001.

৫ · Packet-এর ভেতরে — এনক্রিপ্ট করা header, frame আর varint

একটি TCP segment-এর header — port, sequence, ACK, flag, window, option — সবই খোলা, TLS ব্যবহার করলেও। TLS শুধু ভেতরের ডেটা ঢাকে। QUIC-এ উল্টো: UDP header ছাড়া প্রায় সব এনক্রিপ্টেড বা সুরক্ষিত। একটি 1-RTT packet-এর short header দেখুন:

1-RTT packet (short header), RFC 9000 §17.3
┌─┬─┬─┬───┬─┬───┐
│0│1│S│R R│K│P P│   প্রথম বাইট: form=0 (short), fixed=1, S = spin bit
└─┴─┴─┴───┴─┴───┘   R R K P P → header protection-এ ঢাকা
┌──────────────────────────────┐
│ Destination Connection ID    │   0–20 বাইট, খোলা (router/LB চিনবে)
├──────────────────────────────┤
│ Packet Number (1–4 বাইট)     │   header protection-এ ঢাকা
├──────────────────────────────┤
│ Protected payload: frames …  │   AEAD দিয়ে এনক্রিপ্টেড + যাচাই-ট্যাগ
└──────────────────────────────┘

পথের একটি middlebox দেখতে পায় শুধু UDP port, প্রথম বাইটের কয়েকটি bit, আর Destination Connection ID. Packet number পর্যন্ত লুকানো (header protection). ফলে কেউ “QUIC-এর sequence দেখে কিছু করব” ভেবে যন্ত্র বানাতে পারে না — যা ossification-এর বিরুদ্ধে সবচেয়ে শক্ত ঢাল। আর payload শুধু গোপনই নয়, যাচাইকৃতও: পথে কেউ একটি bit বদলালে প্রাপক packet ফেলে দেয়। Long header-এ এর সাথে খোলা থাকে version, দুই দিকের connection ID আর তাদের দৈর্ঘ্য।

Frame — packet-এর ভেতরের আসল বার্তা

QUIC packet হলো একটি খাম; ভেতরে এক বা একাধিক frame, প্রতিটি একটি নির্দিষ্ট কাজের। একই packet-এ ভিন্ন stream-এর ডেটা, ACK আর flow control-এর হালনাগাদ একসাথে যেতে পারে। প্রধান frame-গুলো:

Frame (type)কাজ
PADDING (0x00), PING (0x01)datagram বড় করা; সংযোগ সজীব রাখা বা ACK চাওয়া
ACK (0x02–0x03)কোন কোন packet number পৌঁছেছে — range হিসেবে, সাথে ack delay (অংশ ৬)
RESET_STREAM (0x04), STOP_SENDING (0x05)একটি stream মাঝপথে বাতিল — বাকি সংযোগ অক্ষত
CRYPTO (0x06)TLS handshake-এর বার্তা
NEW_TOKEN (0x07)পরের সংযোগে ঠিকানা-প্রমাণের token
STREAM (0x08–0x0f)একটি stream-এর ডেটা: stream ID, offset, দৈর্ঘ্য, শেষ কি না (FIN)
MAX_DATA (0x10), MAX_STREAM_DATA (0x11), MAX_STREAMS (0x12–0x13)flow control: পুরো সংযোগে, এক stream-এ কত বাইট, আর কয়টি stream খোলা যাবে
NEW_CONNECTION_ID (0x18), RETIRE_CONNECTION_ID (0x19)বাড়তি connection ID দেওয়া আর পুরোনো ফেরত (অংশ ৭)
PATH_CHALLENGE (0x1a), PATH_RESPONSE (0x1b)নতুন পথ যাচাই — ৮ বাইটের এলোমেলো ডেটা, হুবহু ফেরত
CONNECTION_CLOSE (0x1c–0x1d), HANDSHAKE_DONE (0x1e)সংযোগ বন্ধ (ত্রুটির কোডসহ); handshake নিশ্চিত হওয়ার ঘোষণা

খেয়াল করুন STREAM frame-এ offset থাকে — stream-এর কোন বাইট থেকে এই ডেটা। তাই হারানো ডেটা যেকোনো নতুন packet-এ আবার যেতে পারে; প্রাপক offset দেখে stream-এ ঠিক জায়গায় বসায়। Packet-এর নম্বর আর ডেটার অবস্থান এভাবে আলাদা হয়ে গেছে — TCP-তে দুটোই একটি sequence number-এ বাঁধা।

Variable-length integer — দুই bit-এ দৈর্ঘ্য

QUIC-এর প্রায় সব সংখ্যা (stream ID, offset, দৈর্ঘ্য, frame type) একটি চতুর পদ্ধতিতে লেখা হয়: variable-length integer (RFC 9000 §16). প্রথম বাইটের ওপরের দুটি bit বলে দেয় সংখ্যাটি কত বাইটের, বাকি bit-গুলোতে মান:

প্রথম দুই bitমোট দৈর্ঘ্যমানের bitসর্বোচ্চ মান
00১ বাইট৬৬৩
01২ বাইট১৪১৬,৩৮৩
10৪ বাইট৩০১,০৭,৩৭,৪১,৮২৩
11৮ বাইট৬২২৬২ − ১
দুটি বাইট: 7b bd
কী দেখছি
bit
ফল
  1. প্রথম বাইট খুলুন। 0x7b-কে bit-এ লিখলে 0111 1011. এখনো জানি না সংখ্যাটি কত বাইটের — উত্তর লুকিয়ে আছে এই বাইটের মাথায়।
  2. ওপরের দুই bit = 01. টেবিল অনুযায়ী 01 মানে মোট ২ বাইট। তাই পরের বাইট 0xbd-ও এই সংখ্যার অংশ; তার পরের বাইট থেকে নতুন ঘর শুরু।
  3. দৈর্ঘ্যের দুই bit মুছে ফেলুন। 0x7b-এর সাথে 0x3f-এর bitwise AND: 0011 1011 = 0x3b. দ্বিতীয় বাইট 0xbd অপরিবর্তিত।
  4. জোড়া দিন। 0x3b আর 0xbd মিলে 0x3bbd = ৩×৪০৯৬ + ১১×২৫৬ + ১১×১৬ + ১৩ = ১২২৮৮ + ২৮১৬ + ১৭৬ + ১৩ = ১৫২৯৩ — RFC 9000-এর উদাহরণের সাথে হুবহু মেলে।

এবার একটি encoder/decoder, RFC-র নিজের উদাহরণগুলো দিয়ে পরীক্ষা। সাথে stream ID পড়া: stream ID-র সবচেয়ে নিচের bit বলে কে stream খুলেছে (০ = ক্লায়েন্ট, ১ = সার্ভার), তার ওপরের bit বলে দিক (০ = দুই দিকের, ১ = এক দিকের)। ৮ বাইটের সংখ্যা JavaScript-এর সাধারণ number-এ নিখুঁত ধরে না (২৫৩-এর বেশি), তাই কোডে BigInt.

// QUIC variable-length integer (RFC 9000 section 16): the top 2 bits of the first byte
// give the length: 00 -> 1 byte, 01 -> 2, 10 -> 4, 11 -> 8; the other bits hold the value.
function decode(hex) {
  const bytes = hex.match(/../g).map(h => BigInt("0x" + h));
  const len = 1 << Number(bytes[0] >> 6n);               // 1, 2, 4 or 8
  let v = bytes[0] & 0x3fn;                               // drop the 2 prefix bits
  for (let i = 1; i < len; i++) v = (v << 8n) | bytes[i];
  return { len, v };
}
function encode(v) {
  v = BigInt(v);
  const [len, pfx] = v < 64n ? [1, 0n] : v < 16384n ? [2, 1n] : v < 1073741824n ? [4, 2n] : [8, 3n];
  let out = "";
  for (let i = len - 1; i >= 0; i--) {
    let b = (v >> BigInt(8 * i)) & 0xffn;
    if (i === len - 1) b |= pfx << 6n;                    // put the length prefix on top
    out += b.toString(16).padStart(2, "0");
  }
  return out;
}
for (const h of ["25", "4025", "7bbd", "9d7f3e7d", "c2197c5eff14e88c"]) {
  const { len, v } = decode(h);
  console.log(`decode ${h.padEnd(16)} -> ${len} byte(s), value ${v}   re-encode: ${encode(v)}`);
}
console.log("largest 1/2/4-byte values:", 63, 16383, 1073741823);
// Stream ID: bit 0 = who opened it, bit 1 = direction; id >> 2 = its number within that type
for (const id of [0, 1, 2, 3, 4, 8, 11]) {
  const who = id & 1 ? "server" : "client", dir = id & 2 ? "uni" : "bidi";
  console.log(`stream ${String(id).padStart(2)}: low bits ${(id & 3).toString(2).padStart(2, "0")} -> ${who}-initiated ${dir}, #${id >> 2} of its type`);
}

আউটপুট:

decode 25               -> 1 byte(s), value 37   re-encode: 25
decode 4025             -> 2 byte(s), value 37   re-encode: 25
decode 7bbd             -> 2 byte(s), value 15293   re-encode: 7bbd
decode 9d7f3e7d         -> 4 byte(s), value 494878333   re-encode: 9d7f3e7d
decode c2197c5eff14e88c -> 8 byte(s), value 151288809941952652   re-encode: c2197c5eff14e88c
largest 1/2/4-byte values: 63 16383 1073741823
stream  0: low bits 00 -> client-initiated bidi, #0 of its type
stream  1: low bits 01 -> server-initiated bidi, #0 of its type
stream  2: low bits 10 -> client-initiated uni, #0 of its type
stream  3: low bits 11 -> server-initiated uni, #0 of its type
stream  4: low bits 00 -> client-initiated bidi, #1 of its type
stream  8: low bits 00 -> client-initiated bidi, #2 of its type
stream 11: low bits 11 -> server-initiated uni, #2 of its type

পাঁচটি উদাহরণই RFC 9000-এর পরিশিষ্ট A.1-এর সাথে মেলে: 25 → ৩৭, 7bbd → ১৫২৯৩, 9d7f3e7d → ৪৯৪৮৭৮৩৩৩, আর ৮ বাইটের c2197c5eff14e88c → ১৫১২৮৮৮০৯৯৪১৯৫২৬৫২. মজার লাইনটি 4025: এটিও ৩৭, অকারণে ২ বাইটে লেখা — বৈধ, তবে encoder সবচেয়ে ছোট রূপ বাছে, তাই re-encode-এ আবার 25. Stream 0, 4, 8 সবই ক্লায়েন্টের দুই-দিকের (নিচের দুই bit 00), সেই ধরনের ০, ১, ২ নম্বর — তাই HTTP/3-এ ক্লায়েন্টের অনুরোধ যায় stream 0, 4, 8, 12…-তে। 11-এর নিচের দুই bit 11: সার্ভারের এক-দিকের stream, ২ নম্বর (11 >> 2 = 2).

৬ · Stream, flow control আর নির্ভরযোগ্যতা — TCP-র কাজ নতুন করে

স্বাধীন stream

একটি QUIC সংযোগে অনেক stream থাকতে পারে, আর stream খুলতে handshake লাগে না — নতুন ID-তে প্রথম STREAM frame পাঠালেই হলো। প্রতিটি stream নিজের ভেতরে ক্রম রাখে, stream-গুলোর মাঝে নয়। দুই রকম stream: দুই-দিকের (HTTP/3-এর অনুরোধ আর উত্তর) আর এক-দিকের (যে খোলে শুধু সে পাঠায়; HTTP/3-এর নিয়ন্ত্রণ-বার্তা আর QPACK). মোট সংখ্যা সীমিত থাকে প্রাপকের দেওয়া MAX_STREAMS-এ।

দুই স্তরের flow control

পর্ব ৩১-এ দেখেছেন TCP-র flow control: প্রাপক জানায় তার buffer-এ কতটা জায়গা। QUIC-এ এটি দুই স্তরে: MAX_STREAM_DATA বলে একটি নির্দিষ্ট stream-এ সর্বোচ্চ কোন offset পর্যন্ত পাঠানো যাবে, আর MAX_DATA বলে পুরো সংযোগে সব stream মিলিয়ে কত। কেন দুটোই? শুধু stream-স্তরের সীমা থাকলে হাজারটি stream খুলে প্রাপকের memory ভরিয়ে ফেলা যেত; শুধু সংযোগ-স্তরের থাকলে একটি বড় stream পুরো সীমা খেয়ে ফেলে অন্যদের থামিয়ে দিত — আবার এক ধরনের head-of-line blocking. প্রেরক সীমায় আটকে গেলে DATA_BLOCKED বা STREAM_DATA_BLOCKED পাঠিয়ে জানায়।

Packet number কখনো ফিরে আসে না — Karn-এর সমস্যার সমাধান

পর্ব ৩০-এ দেখেছেন TCP retransmission timeout ঠিক করতে RTT মাপে: segment পাঠানোর সময় আর তার ACK আসার সময়ের পার্থক্য। কিন্তু একটি segment দুবার পাঠানো হলে (একই sequence number-এ) আর তারপর ACK এলে — এটি কোন পাঠানোর ACK? প্রথমটির হলে RTT অনেক বড়, দ্বিতীয়টির হলে ছোট। প্রেরকের জানার উপায় নেই; একে বলে retransmission ambiguity. তাই Karn-এর algorithm নিয়ম করে: পুনঃপাঠানো segment-এর RTT নমুনা বাদ দিন। ফল: ঠিক যখন নেটওয়ার্ক খারাপ, তখনই RTT-র নমুনা কম।

QUIC সমস্যাটির মূল কেটে দিয়েছে: প্রতিটি পাঠানো packet নতুন packet number পায়, কখনো পুনরাবৃত্তি নয়। হারানো ডেটা আবার যায়, কিন্তু নতুন নম্বরের packet-এ (অংশ ২-এর stepper-এ packet 9). প্রমাণটি সরল: ACK frame packet number-এর নাম ধরে বলে কী পৌঁছেছে; প্রতিটি packet number ঠিক একবার পাঠানো হয়েছে, তাই প্রতিটি নম্বরের একটিমাত্র পাঠানোর সময় আছে। কাজেই “ACK পাওয়ার সময় − সেই নম্বরের পাঠানোর সময়” সবসময় একটি অদ্ব্যর্থ RTT নমুনা — retransmission-এর পরও। ক্ষতি ধরাও সহজ: নম্বর ক্রমাগত বাড়ে, তাই অনেক পরের নম্বর ACK হলে আগের ACK-না-পাওয়া packet হারিয়েছে বলে ধরা যায় (RFC 9002: ৩টি packet পরের নম্বর ACK হলে, বা সময়ের হিসাবে)।

ACK frame: range আর ack delay

TCP-র মূল ACK cumulative — “এই বাইট পর্যন্ত সব পেয়েছি” (পর্ব ৩০); ফাঁকের খবর দিতে লাগে SACK option, যাতে সর্বোচ্চ কয়েকটি ব্লকের জায়গা। QUIC-এর ACK frame শুরু থেকেই range-ভিত্তিক: Largest Acknowledged, ACK Delay, তারপর কয়েকটি পরপর-পাওয়া packet-এর range আর তাদের মাঝের ফাঁক (Gap). ধরুন প্রাপক 0–5 আর 7–9 পেয়েছে, 6 হারিয়েছে। ACK frame: Largest = 9, First ACK Range = 2 (অর্থাৎ 9, 8, 7), তারপর Gap = 0 আর Range Length = 5 (অর্থাৎ 5 থেকে 0). পরের range-এর সবচেয়ে বড় নম্বর = আগের range-এর সবচেয়ে ছোট − Gap − ২ = ৭ − ০ − ২ = ৫. (অনুশীলন ২-এ হাতে করবেন।)

ACK Delay হলো প্রাপক ACK পাঠাতে ইচ্ছাকৃতভাবে কতটা দেরি করেছে (সে কয়েকটি packet জমিয়ে একবারে ACK দেয়)। প্রেরক RTT নমুনা থেকে এটি বাদ দেয়, যাতে প্রাপকের অপেক্ষা নেটওয়ার্কের delay বলে ভুল না হয়। RFC 9002-এর নিয়ম:

latest_rtt = ACK পাওয়ার সময় − largest acked-এর পাঠানোর সময়
adjusted = latest_rtt − ack_delay  (যদি latest_rtt ≥ min_rtt + ack_delay, নইলে latest_rtt)
smoothed_rtt = ⅞ · smoothed_rtt + ⅛ · adjusted
উদাহরণ: packet ৭ পাঠানো ১০০০ ms-এ, ACK এল ১০৯৫-এ, ack delay ১৫, min_rtt ৭০, আগের smoothed ৮৪ → latest ৯৫, ৯৫ ≥ ৮৫ তাই adjusted ৮০, নতুন smoothed = ৭৩.৫ + ১০ = ৮৩.৫ ms

Congestion control — বদলযোগ্য, অ্যাপের ভেতরে

QUIC-ও TCP-র মতো নেটওয়ার্কের সাথে ন্যায্য থাকতে congestion control করে। RFC 9002 উদাহরণ হিসেবে NewReno-ধাঁচের একটি algorithm বর্ণনা করে — slow start, ক্ষতিতে window অর্ধেক, তারপর ধীরে বাড়ানো (পর্ব ৩২) — প্রাথমিক window ১০টি datagram-এর মতো (সঠিক সূত্র: ১০ × max datagram size, তবে ১৪,৭২০ বাইট বা দুটি datagram-এর বড়টির বেশি নয়)। কিন্তু RFC কোনো একটি algorithm বাধ্যতামূলক করে না; বাস্তবের বাস্তবায়নগুলো CUBIC বা BBR-ও ব্যবহার করে। আর যেহেতু এটি অ্যাপের ভেতরে, নতুন algorithm পরীক্ষা করতে kernel বদলাতে হয় না।

৭ · Connection ID আর migration — বাসা বদলালেও সংযোগ টেকে

প্রতিটি QUIC packet-এ একটি connection ID (CID) থাকে — দুই পক্ষ নিজেরা বেছে নেয় অপর পক্ষ তাকে কোন ID দিয়ে ডাকবে। সার্ভার যখন packet পায়, সে 4-tuple নয়, Destination Connection ID দেখে সংযোগ খোঁজে। ফলে উৎস IP বা port বদলালেও সংযোগ চেনা যায়। এটাই connection migration.

তবে নতুন ঠিকানায় অন্ধভাবে ডেটা পাঠানো বিপজ্জনক: কেউ অন্যের connection ID নকল করে উৎস হিসেবে একজন নিরীহ ব্যক্তির ঠিকানা বসালে সার্ভার সেই ব্যক্তির দিকে বিশাল ডাউনলোড পাঠাতে শুরু করত। তাই QUIC নতুন পথ ব্যবহারের আগে path validation করে: নতুন ঠিকানায় PATH_CHALLENGE frame-এ ৮ বাইটের অনুমান-অযোগ্য এলোমেলো ডেটা পাঠায়; সত্যিকারের ক্লায়েন্ট সেখানে থাকলে PATH_RESPONSE-এ হুবহু সেই ৮ বাইট ফেরত দেয়। যাচাইয়ের আগে সেই ঠিকানায় প্রাপ্ত বাইটের ৩ গুণের বেশি পাঠানো যায় না।

  1. Wi-Fi পথ198.51.100.7:40001DCID c1a2
  2. মোবাইল পথ203.0.113.77:62000DCID 7f3e
  3. সার্ভারPATH_CHALLENGE9b4e01d2…
  4. ক্লায়েন্টPATH_RESPONSEহুবহু ফেরত
সার্ভার 192.0.2.10:443-এর দৃষ্টিতে
সময়
Packet-এর উৎস
TCP হলে
QUIC: CID খোঁজা
QUIC: পথের অবস্থা
  1. Wi-Fi-তে ডাউনলোড চলছে। ফোনের packet বাসার NAT পেরিয়ে সার্ভারে আসে 198.51.100.7:40001 থেকে, Destination Connection ID c1a2. আগেই সার্ভার NEW_CONNECTION_ID frame দিয়ে একটি বাড়তি ID 7f3e দিয়ে রেখেছে। TCP হলে সার্ভার সংযোগটি চিনত শুধু 4-tuple দিয়ে।
  2. ফোন বাসা থেকে বেরোল। Wi-Fi হারিয়ে মোবাইল ডেটা; অপারেটরের CGNAT পেরিয়ে নতুন উৎস 203.0.113.77:62000. TCP-র চোখে এটি সম্পূর্ণ অচেনা 4-tuple — সার্ভার RST পাঠায়। QUIC ক্লায়েন্ট নতুন পথে নতুন CID 7f3e ব্যবহার করে, যাতে পথের পর্যবেক্ষক পুরোনো আর নতুন পথের packet একই ব্যক্তির বলে মেলাতে না পারে (গোপনীয়তা)।
  3. সার্ভার চিনল, কিন্তু যাচাই করবে। CID 7f3e দেখে সার্ভার জানে এটি download #1. ঠিকানা নতুন, তাই PATH_CHALLENGE পাঠায় ৮ বাইটের এলোমেলো ডেটা দিয়ে — এখানে 9b4e01d27a55c3f8. যাচাইয়ের আগে এই ঠিকানায় প্রাপ্ত বাইটের ৩ গুণের বেশি পাঠায় না। TCP-র দিকে অ্যাপ এখন ভাঙা সংযোগ টের পেয়ে নতুন করে শুরু করছে।
  4. ক্লায়েন্ট প্রমাণ দিল। ফোন PATH_RESPONSE-এ হুবহু একই ৮ বাইট ফেরত দিল। যে ঠিকানায় challenge গেছে, সেখানে সত্যিই এই ক্লায়েন্ট আছে — প্রমাণ হলো। সার্ভার নতুন পথের congestion window আর RTT-র হিসাব শুরু থেকে করে, কারণ মোবাইল পথের ক্ষমতা Wi-Fi-র মতো নাও হতে পারে (শুধু port বদলালে, যেমন NAT rebinding-এ, এটি বাধ্যতামূলক নয়)।
  5. ডাউনলোড চলতেই থাকল। একই সংযোগ, একই stream, একই offset থেকে — কোনো নতুন handshake নেই। ক্লায়েন্ট পুরোনো c1a2 RETIRE_CONNECTION_ID দিয়ে ছেড়ে দেয়। TCP-র দিকে তখনো নতুন handshake শেষ হয়নি, আর অ্যাপকে নিজেই মনে রাখতে হবে ডাউনলোড কোথায় থেমেছিল।

একই কাহিনি একটি simulator-এ, যেখানে সার্ভার দুই রকম টেবিলে সংযোগ খোঁজে: TCP-র জন্য 4-tuple, QUIC-এর জন্য connection ID.

// A phone moves from Wi-Fi to mobile data in the middle of a download.
// A TCP server finds the connection by its 4-tuple; a QUIC server by the Destination Connection ID.
const SRV = "192.0.2.10:443";
const tcpConns = new Map([["198.51.100.7:40001>" + SRV, "download #1"]]);
const quicConns = new Map([["c1a2", "download #1"], ["7f3e", "download #1"]]); // 7f3e: spare CID issued earlier
const validated = new Set(["198.51.100.7:40001"]), challenge = new Map();
const pkts = [
  { t: 0,   from: "198.51.100.7:40001", dcid: "c1a2", note: "data on Wi-Fi" },
  { t: 100, from: "203.0.113.77:62000", dcid: "7f3e", note: "data, now on mobile data" },
  { t: 140, from: "203.0.113.77:62000", dcid: "7f3e", note: "PATH_RESPONSE", echo: "9b4e01d27a55c3f8" },
  { t: 150, from: "203.0.113.77:62000", dcid: "7f3e", note: "data, next request" },
];
for (const p of pkts) {
  const tcp = tcpConns.get(p.from + ">" + SRV), conn = quicConns.get(p.dcid);
  console.log(`t=${p.t} ms  from ${p.from}  [${p.note}]`);
  console.log(`  TCP : ${tcp ? "4-tuple matches " + tcp : "unknown 4-tuple -> RST; app must open a new connection"}`);
  if (!conn) { console.log("  QUIC: unknown connection ID -> drop"); continue; }
  let q = `CID ${p.dcid} -> ${conn}; `;
  if (p.note === "PATH_RESPONSE") {
    const ok = challenge.get(p.from) === p.echo;
    if (ok) validated.add(p.from);
    q += ok ? "echo matches -> path validated; reset cwnd and RTT estimate" : "wrong echo -> ignore";
  } else if (!validated.has(p.from)) {
    challenge.set(p.from, "9b4e01d27a55c3f8");    // in real QUIC: 8 unpredictable random bytes
    q += "new address -> PATH_CHALLENGE 9b4e01d27a55c3f8, send at most 3x bytes received";
  } else q += "validated path -> continue";
  console.log("  QUIC: " + q);
}

আউটপুট:

t=0 ms  from 198.51.100.7:40001  [data on Wi-Fi]
  TCP : 4-tuple matches download #1
  QUIC: CID c1a2 -> download #1; validated path -> continue
t=100 ms  from 203.0.113.77:62000  [data, now on mobile data]
  TCP : unknown 4-tuple -> RST; app must open a new connection
  QUIC: CID 7f3e -> download #1; new address -> PATH_CHALLENGE 9b4e01d27a55c3f8, send at most 3x bytes received
t=140 ms  from 203.0.113.77:62000  [PATH_RESPONSE]
  TCP : unknown 4-tuple -> RST; app must open a new connection
  QUIC: CID 7f3e -> download #1; echo matches -> path validated; reset cwnd and RTT estimate
t=150 ms  from 203.0.113.77:62000  [data, next request]
  TCP : unknown 4-tuple -> RST; app must open a new connection
  QUIC: CID 7f3e -> download #1; validated path -> continue

t=100-এ উৎস বদলাতেই TCP-র টেবিলে কিছু নেই — “unknown 4-tuple -> RST”, পরের প্রতিটি packet-এও একই ফল। QUIC CID 7f3e দিয়ে সঙ্গে সঙ্গে download #1 খুঁজে পায়, কিন্তু ঠিকানা যাচাইকৃত নয় বলে PATH_CHALLENGE পাঠায়; t=140-এ ফেরত ডেটা মেলে, পথ যাচাইকৃত হয়, আর t=150-এর অনুরোধ স্বাভাবিক চলে। বাস্তবে challenge-এর ৮ বাইট প্রতিবার নতুন এলোমেলো; এখানে নির্দিষ্ট রাখা হয়েছে যাতে আউটপুট একই থাকে।

NAT rebinding, keepalive আর spin bit

পর্ব ১৯-এ দেখেছেন NAT-এর UDP ম্যাপিং অনেক সময় কয়েক দশ সেকেন্ডের নীরবতাতেই মুছে যায়, আর পরের packet নতুন বাইরের port পায়। QUIC-এর চোখে এটি একটি ছোট্ট migration: CID একই, শুধু port বদলেছে। তবু দীর্ঘ নীরব সংযোগে অ্যাপ মাঝে মাঝে PING frame পাঠায় (keepalive). লক্ষ করুন, RFC 9000-এ শুধু ক্লায়েন্টই সক্রিয়ভাবে ঠিকানা বদলাতে পারে; সার্ভার চাইলে transport parameter দিয়ে migration নিষেধও করতে পারে।

Header এনক্রিপ্ট হওয়ায় নেটওয়ার্ক অপারেটর TCP-র মতো sequence আর ACK দেখে RTT মাপতে পারেন না। আপস হিসেবে short header-এ একটি ঐচ্ছিক spin bit রাখা হয়েছে: দুই প্রান্ত মিলে bit-টি প্রতি RTT-তে একবার উল্টায়, ফলে পথের একটি যন্ত্র শুধু এই bit বদলানোর সময়ের ব্যবধান দেখে RTT আন্দাজ করতে পারে — ভেতরের কিছু না দেখেই। কোনো প্রান্ত চাইলে এটি নিষ্ক্রিয় রাখতে পারে।

৮ · নিরাপত্তা, বাস্তব প্রয়োগ আর অন্য আধুনিক transport

Anti-amplification আর Retry

UDP-তে উৎস ঠিকানা জাল করা সহজ (IP spoofing). কেউ একজন নিরীহ ব্যক্তির ঠিকানা দিয়ে ছোট অনুরোধ পাঠালে, আর সার্ভার বড় উত্তর (certificate-সহ) সেই ব্যক্তিকে পাঠালে, সার্ভার নিজেই আক্রমণের হাতিয়ার হয়ে যায় — একে বলে amplification attack, DNS-এর মতো UDP সেবায় যা বহুবার দেখা গেছে (প্রতিরক্ষার বিস্তারিত পর্ব ৫০)। QUIC-এর প্রতিরক্ষা দুটি নিয়মে:

  • Anti-amplification limit: ক্লায়েন্টের ঠিকানা যাচাইয়ের আগে সার্ভার সেই ঠিকানা থেকে যত বাইট পেয়েছে তার ৩ গুণের বেশি পাঠাবে না। ক্লায়েন্ট Handshake packet সফলভাবে পাঠালে (অর্থাৎ সার্ভারের key ব্যবহার করতে পারলে) ঠিকানা যাচাইকৃত ধরা হয়।
  • Initial অন্তত ১২০০ বাইট: ক্লায়েন্টের Initial packet-বাহী datagram PADDING দিয়ে অন্তত ১২০০ বাইট করতে হয়। তাতে সার্ভার প্রথম উত্তরে অন্তত ৩,৬০০ বাইট পাঠাতে পারে — সাধারণত certificate পাঠানোর জন্য যথেষ্ট — আর আক্রমণকারীর জন্য প্রতি ১ বাইটে সর্বোচ্চ ৩ বাইটের লাভ, যা তেমন আকর্ষণীয় নয়। সাথে বোঝা যায় পথটি ১২০০ বাইটের datagram বহন করতে পারে, যা QUIC-এর ন্যূনতম শর্ত।

চাপের সময় (যেমন অনেক জাল Initial আসছে) সার্ভার কোনো অবস্থা না রেখে একটি Retry packet-এ token পাঠায়; সত্যিকারের ক্লায়েন্ট সেই token নিয়ে আবার Initial পাঠিয়ে প্রমাণ দেয় যে সে ওই ঠিকানায় আছে। খরচ এক RTT, যা আগের সংযোগে NEW_TOKEN-এ পাওয়া token দেখিয়ে বাঁচানো যায়।

ব্রাউজার কীভাবে জানে HTTP/3 আছে

ব্রাউজার প্রথমবার একটি সাইটে সাধারণত TCP-তে (HTTP/2) যায়। সার্ভার উত্তরে একটি Alt-Svc header দেয়, যেমন alt-svc: h3=":443"; ma=86400 — “একই জিনিস UDP port 443-এ HTTP/3-তেও পাবেন, এই তথ্য এক দিন মনে রাখবেন”. পরের অনুরোধ থেকে ব্রাউজার QUIC চেষ্টা করে। আরও আগে জানার উপায় DNS-এর HTTPS record (RFC 9460, ২০২৩), যাতে alpn="h3,h2" লেখা থাকতে পারে — তাহলে প্রথম সংযোগেই QUIC. দুটির বিস্তারিত পর্ব ৩৭-এ।

$ curl -sI https://www.example.com/ | grep -i '^alt-svc'
alt-svc: h3=":443"; ma=86400

$ dig +short www.example.com HTTPS
1 . alpn="h3,h2"

$ curl --http3 -sI https://www.example.com/ | head -1     # curl-কে HTTP/3 সমর্থনসহ build করা থাকলে
HTTP/3 200

কোনো নেটওয়ার্ক UDP 443 আটকে রাখলে (কিছু অফিস বা পাবলিক Wi-Fi) QUIC-এর handshake উত্তর পায় না; ব্রাউজার তখন — বা শুরু থেকেই সমান্তরাল চেষ্টায় — TCP-তে HTTP/2-এ ফিরে যায়। ব্যবহারকারী টের পান না, শুধু সুবিধাটা পান না। তাই সাইটগুলো TCP-ও চালু রাখে।

কতটা চলে, আর কারা চালায়

প্রধান ব্রাউজারগুলো (Chrome, Firefox, Safari, Edge) HTTP/3 সমর্থন করে, আর Google, Meta, বড় CDN প্রতিষ্ঠানগুলো ব্যাপকভাবে QUIC চালায়। বিভিন্ন পরিমাপে ওয়েব ট্রাফিকের একটি উল্লেখযোগ্য অংশ এখন QUIC-এ, তবে সঠিক অংশ মাপার পদ্ধতি, অঞ্চল আর সময়ভেদে অনেক বদলায় — কোনো একটি সংখ্যাকে চূড়ান্ত ধরবেন না। বাংলাদেশের প্রেক্ষাপটে আন্তর্জাতিক কনটেন্টের RTT বেশি আর মোবাইল নেটওয়ার্কে loss ও ঠিকানা-বদল সাধারণ — ঠিক যেখানে QUIC-এর সুবিধা বেশি; যদিও দেশের ভেতরের CDN cache আর BDIX-এর মতো আদান-প্রদান কেন্দ্র অনেক সাইটের RTT এমনিতেই কমায়।

খরচ: CPU আর UDP-র পথ

TCP-কে kernel বহু দশক ধরে দ্রুত করেছে: NIC নিজেই বড় ডেটাকে segment-এ ভাগ করে (TSO), প্রাপকের দিকে জোড়া দেয় (GRO). QUIC শুরুতে এসব পায়নি: প্রতিটি datagram-এর জন্য আলাদা system call, user space-এ এনক্রিপশন — তাই একই ডেটায় বেশি CPU খরচের পরিমাপ বহুবার প্রকাশিত হয়েছে। সমাধান: UDP GSO (Linux-এ ৪.১৮ নাগাদ থেকে), যেখানে একটি বড় buffer kernel বা NIC অনেক datagram-এ কাটে; sendmmsg/recvmmsg; আর UDP-র GRO. পার্থক্য অনেক কমেছে, তবে শূন্য নয়।

অপারেটরের দৃষ্টিতে এনক্রিপ্টেড header — দুই দিকের যুক্তি

এনক্রিপশন গোপনীয়তা বাড়ায় আর ossification ঠেকায়, কিন্তু অপারেটরদের অসুবিধাও বাস্তব: sequence আর ACK দেখে loss বা RTT মাপা যায় না, satellite লিংকের TCP-ত্বরণকারী proxy কাজ করে না, আর প্রতিষ্ঠানের নিরাপত্তা-যন্ত্র ভেতরে দেখতে পারে না। IETF এই টানাপোড়েন নিয়ে RFC 9312 লিখেছে; spin bit সেই আপসের ফল। দুই পক্ষের যুক্তি ভাবনার প্রশ্ন ২-এ।

অন্য আধুনিক transport — এক নজরে

নামমূল ধারণাকোথায় দেখবেন
MPTCP (RFC 8684)একটি TCP সংযোগ একাধিক পথে (Wi-Fi + মোবাইল) একসাথে; বাইরে থেকে সাধারণ TCP-র মতো দেখায়Linux kernel-এ (৫.৬ নাগাদ থেকে মূলধারায়); Apple কিছু সেবায় ব্যবহার করে বলে জানা
SCTP (RFC 9260)বার্তা-ভিত্তিক, একাধিক stream আর একাধিক ঠিকানা (multi-homing) — QUIC-এর অনেক আগের ধারণামোবাইল অপারেটরের ভেতরের signaling নেটওয়ার্ক; WebRTC-র data channel (DTLS-এর ভেতরে, UDP-র ওপর)
WebTransportব্রাউজারের API, HTTP/3-এর ওপর একাধিক stream আর অনির্ভরযোগ্য datagramগেম, লাইভ স্ট্রিমিং; মান এখনো বিকাশমান (W3C ও IETF খসড়া)
QUIC DATAGRAM (RFC 9221), MASQUEQUIC-এর ভেতরে অনির্ভরযোগ্য কিন্তু এনক্রিপ্টেড datagram; তার ওপর UDP proxy (RFC 9298)VPN আর গোপনীয়তা-relay ধরনের সেবা
Multipath QUICএকটি QUIC সংযোগ একসাথে একাধিক পথে — migration-এর চেয়ে এক ধাপ বেশিIETF-এ খসড়া পর্যায়ে, চূড়ান্ত মান হয়নি (লেখার সময় পর্যন্ত)

আরও গভীরে: QUIC-এর header protection-এর কৌশলটি সুন্দর: এনক্রিপ্ট করা payload থেকে ১৬ বাইটের একটি নমুনা নিয়ে আলাদা key দিয়ে একটি mask বানানো হয়, আর সেই mask দিয়ে packet number আর প্রথম বাইটের নিচের bit-গুলো XOR করা হয় (RFC 9001 §5.4). Packet number-ও পুরো পাঠানো হয় না — মাত্র ১–৪ বাইট, প্রাপক তার জানা সবচেয়ে বড় নম্বরের কাছাকাছি মান ধরে পুরোটা পুনর্গঠন করে। আর অপরিচিত সংস্করণ ও bit-এর জন্য “GREASE” — ইচ্ছাকৃত এলোমেলো মান পাঠানো, যাতে কোনো middlebox নির্দিষ্ট মানে অভ্যস্ত না হয় — TLS (RFC 8701) থেকে QUIC-এও এসেছে।

পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • QUIC কেন TCP-র বদলে UDP-র ওপর? — UDP প্রায় সব NAT/firewall পেরোয়, আর user space-এ বাস্তবায়ন করে kernel ছাড়াই দ্রুত হালনাগাদ করা যায়; নির্ভরযোগ্যতা QUIC নিজে দেয়।
  • HTTP/2-এর head-of-line blocking QUIC কীভাবে দূর করে? — stream-গুলো transport স্তরেই স্বাধীন; এক stream-এর ক্ষতিতে শুধু সেই stream অপেক্ষা করে, অন্যরা নয়।
  • নতুন সংযোগে প্রথম বাইট পর্যন্ত কত RTT? — TCP + TLS 1.2: ৪, TCP + TLS 1.3: ৩, QUIC: ২, QUIC 0-RTT: ১ (প্রক্রিয়াকরণ বাদে)।
  • Connection migration কী, কেন নিরাপদ? — connection ID দিয়ে সংযোগ চেনা, তাই IP/port বদলালেও চলে; PATH_CHALLENGE/RESPONSE দিয়ে নতুন পথ যাচাই আর ৩ গুণ সীমা।
  • 0-RTT-এর ঝুঁকি? — replay; তাই শুধু idempotent অনুরোধ, single-use ticket, আর HTTP-তে 425 Too Early.

৯ · AI-এর যুগে QUIC — শেখা, মাপা আর যাচাই

QUIC তুলনামূলক নতুন, আর তার আগের Google-সংস্করণ নিয়ে পুরোনো লেখা প্রচুর। AI তাই অনেক সময় gQUIC আর IETF QUIC মিশিয়ে ফেলে, বা আত্মবিশ্বাসের সাথে ভুল সংখ্যা বলে। চেনা ভুলগুলো:

AI-এর ভুলআসল কথাকীভাবে যাচাই করবেন
“QUIC UDP-তে চলে, তাই ডেটা হারাতে পারে”QUIC নিজেই ACK, retransmission আর ক্রম রাখে; stream নির্ভরযোগ্যRFC 9000-এর stream অংশ আর RFC 9002 পড়ুন
“QUIC সব head-of-line blocking দূর করে”শুধু stream-এর মাঝেরটা; stream-এর ভেতরে ক্রম আছেঅংশ ২-এর simulator-এ একই stream-এ ক্ষতি বসিয়ে দেখুন
“QUIC সবসময় 0-RTT”0-RTT শুধু আগের session থাকলে, আর replay-ঝুঁকিসহRFC 9001 আর RFC 8446-এর 0-RTT অংশ
ভুল RFC নম্বর (যেমন HTTP/3-কে RFC 9000 বলা) বা QUIC Crypto-কে আজকের মান বলা9000 transport, 9001 TLS, 9002 recovery, 9114 HTTP/3; IETF QUIC TLS 1.3 ব্যবহার করেrfc-editor.org-এ নম্বর আর শিরোনাম মেলান
“QUIC চলছে না? firewall-এ সব UDP খুলে দিন”শুধু দরকারি UDP 443, তাও নিজের নীতি মেনে; সব খোলা বিপজ্জনকনিরাপত্তা দলের সাথে নীতি মিলিয়ে নিন, পর্ব ৪৯

AI-কে QUIC বোঝাতে বা মাপতে বললে নির্দিষ্ট করুন কোন সংস্করণ, আর যাচাইয়ের সূত্র চেয়ে নিন:

তুমি একজন নেটওয়ার্ক প্রকৌশলীর মতো উত্তর দাও। IETF QUIC version 1 (RFC 9000, 9001, 9002) নিয়ে প্রশ্ন — Google-এর পুরোনো gQUIC নয়। ১. RTT ৯০ ms ধরে TCP+TLS 1.2, TCP+TLS 1.3, QUIC 1-RTT আর QUIC 0-RTT-এ প্রথম বাইট পর্যন্ত সময় হিসাব করো, প্রতিটি RTT কোথা থেকে এল দেখাও। ২. varint 0x9d7f3e7d ধাপে ধাপে decode করো, আর stream ID 6 ও 13-এর অর্থ বলো। ৩. আমার ল্যাবের নিজের ব্রাউজারের QUIC ট্রাফিক Wireshark-এ কীভাবে দেখব, SSLKEYLOGFILE দিয়ে নিজের session decrypt করার ধাপ দাও। ৪. প্রতিটি দাবির পাশে RFC নম্বর আর section দাও; নিশ্চিত না হলে স্পষ্ট বলো। ৫. Firewall বা নিরাপত্তা-সংক্রান্ত পরামর্শ দিলে ঝুঁকি আলাদা করে লেখো।

AI-এর QUIC-ব্যাখ্যা বা কনফিগারেশন বিশ্বাস করার আগে — চেকলিস্ট

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

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

প্র ০১Google কেন TCP সারানোর বদলে UDP-র ওপর, user space-এ একটি নতুন protocol বানাল? এই সিদ্ধান্তের লাভ আর দীর্ঘমেয়াদি দাম কী?

২০১০-এর দশকের শুরুতে Google-এর প্রকৌশলীরা TCP-কে উন্নত করার কয়েকটি চেষ্টা দেখছিলেন। প্রাথমিক congestion window বাড়ানোর প্রস্তাব, TCP Fast Open, আরও কিছু kernel পরিবর্তন — এগুলো মান হতে আর ব্যবহারকারীর যন্ত্রে পৌঁছাতে বছর লাগছিল। সার্ভারের kernel তারা নিজেরা বদলাতে পারতেন, কিন্তু ক্লায়েন্টের দিক — Windows, পুরোনো Android, অফিসের পুরোনো Linux — তাঁদের হাতে ছিল না। আর যতটুকু পৌঁছাত, পথের middlebox অনেক সময় নষ্ট করে দিত: TCP Fast Open-এর ডেটাসহ SYN কিছু নেটওয়ার্কে হারিয়ে যেত, ফলে ব্রাউজারকে সতর্ক fallback রাখতে হতো।

অন্যদিকে Chrome কয়েক সপ্তাহ পরপর হালনাগাদ হয়, আর Google-এর সার্ভারও তাদের নিয়ন্ত্রণে। Protocol-টি যদি দুই প্রান্তের অ্যাপের ভেতরে থাকে, তাহলে একটি নতুন ধারণা কয়েক সপ্তাহে কোটি মানুষের কাছে পৌঁছাতে পারে, A/B পরীক্ষা করে মাপা যায় সত্যিই লাভ হচ্ছে কি না, আর ভুল হলে ফিরিয়েও নেওয়া যায়। UDP বেছে নেওয়া হলো কারণ এটি NAT আর firewall পেরোয়, আর header এনক্রিপ্ট করা হলো যাতে এই নতুন protocol-ও middlebox-এর অভ্যাসে আটকে না যায়। IETF-এর মান হওয়ার সময় সবচেয়ে বড় পরিবর্তন ছিল Google-এর নিজস্ব crypto বাদ দিয়ে TLS 1.3 নেওয়া — যাতে নিরাপত্তা-বিশ্লেষণ আর বাস্তবায়ন সবার জন্য একই ভিত্তিতে হয়।

দাম কী? প্রথমত, প্রতিটি অ্যাপ বা লাইব্রেরি নিজের QUIC বহন করে; একটি যন্ত্রে হয়তো কয়েকটি আলাদা বাস্তবায়ন, আলাদা বাগ, আলাদা congestion control — যা kernel-এর একক TCP-তে ছিল না। দ্বিতীয়ত, CPU-র খরচ: kernel আর NIC বহু দশক ধরে TCP-র জন্য যে ত্বরণ শিখেছে, UDP-র জন্য তা নতুন করে বানাতে হচ্ছে। তৃতীয়ত, নেটওয়ার্ক অপারেটরদের দৃশ্যমানতা কমেছে। আর একটি সূক্ষ্ম ঝুঁকি: যে কোম্পানি ব্রাউজার আর বড় সার্ভার দুটোই নিয়ন্ত্রণ করে, সে প্রায় একা একটি protocol চালু করতে পারে — IETF-এর উন্মুক্ত প্রক্রিয়া এই ক্ষমতাকে ভারসাম্যে রাখে।

মূল উপলব্ধি: Protocol-এর নকশা শুধু প্রযুক্তি নয়, হালনাগাদের পথও। QUIC-এর সবচেয়ে বড় উদ্ভাবন হয়তো কোনো একক বৈশিষ্ট্য নয় — বরং এমন জায়গায় transport রাখা, যেখান থেকে ভবিষ্যতে বদলানো যায়।

প্র ০২QUIC transport header প্রায় পুরোটা এনক্রিপ্ট করেছে। এটি ব্যবহারকারী, নেটওয়ার্ক অপারেটর আর ইন্টারনেটের ভবিষ্যতের জন্য ভালো না খারাপ?

পক্ষের যুক্তি তিনটি স্তরে। ব্যবহারকারীর দিক থেকে: কম তথ্য খোলা থাকলে পথের পর্যবেক্ষক কম জানে — কোন সংযোগ কতক্ষণ, কোন stream-এ কত ডেটা, এমনকি migration-এর পর একই মানুষ কি না (নতুন connection ID-র কারণে)। প্রকৌশলের দিক থেকে: যা দেখা যায় না, তাতে কেউ নির্ভর করতে পারে না; তাই QUIC-এর পরের সংস্করণ নতুন কিছু যোগ করলে কোনো পুরোনো firewall তা ভাঙবে না। “Use it or lose it” ভাবনাও এখান থেকে: কোনো বিস্তারযোগ্য ঘর নিয়মিত বদলাতে না থাকলে তা জমে যায়, তাই GREASE-এর মতো কৌশলে ইচ্ছাকৃত এলোমেলো মান পাঠানো হয়।

বিপক্ষের যুক্তিও গুরুতর। মোবাইল অপারেটর বা ISP বহু বছর ধরে TCP-র sequence আর ACK দেখে নেটওয়ার্কের সমস্যা খুঁজেছেন: কোন অংশে loss, কোথায় RTT বাড়ছে। QUIC-এ সেই জানালা বন্ধ। Satellite-এর মতো অনেক দেরির লিংকে অপারেটররা TCP-কে কৃত্রিমভাবে দ্রুত করতেন (performance-enhancing proxy); QUIC-এ তা অসম্ভব। প্রতিষ্ঠানের নিরাপত্তা দল আইনগত বা নীতিগত কারণে ট্রাফিক পরীক্ষা করতে চায়; তারা অনেক সময় UDP 443 আটকে দিয়ে TCP-তে ফিরিয়ে আনে, যেখানে তাদের proxy কাজ করে।

IETF এই বিতর্ক খোলাখুলি করেছে। spin bit নিয়ে দীর্ঘ আলোচনার পর সেটি ঐচ্ছিক হিসেবে রাখা হয় — RTT আন্দাজের জন্য যথেষ্ট, কিন্তু তার বেশি কিছু নয়, আর প্রান্ত চাইলে বন্ধ রাখতে পারে। RFC 9312 অপারেটরদের জন্য লিখেছে QUIC-এর কী দেখা যায়, কী যায় না, আর কীভাবে লোড ব্যালান্সিং বা DDoS প্রতিরক্ষা চালানো যায়। বড় প্রবণতাটি স্পষ্ট: ইন্টারনেট ধীরে ধীরে “প্রান্তে বুদ্ধি, মাঝে শুধু বহন”-এর দিকে ফিরছে, এবার এনক্রিপশনের জোরে।

মূল উপলব্ধি: এনক্রিপশন এখানে শুধু গোপনীয়তার জন্য নয়, protocol-এর বিবর্তনের স্বাধীনতার জন্য। দাম দিতে হচ্ছে পথের দৃশ্যমানতায় — আর সেই দাম কতটা গ্রহণযোগ্য, তা ঠিক হয় প্রেক্ষাপটে, স্পষ্ট আপসের মাধ্যমে।

প্র ০৩0-RTT এক RTT বাঁচায়, কিন্তু replay-র ঝুঁকি আনে। একটি ব্যাংক বা মোবাইল ব্যাংকিং অ্যাপের সার্ভার হলে আপনি 0-RTT কীভাবে ব্যবহার করবেন?

প্রথমে ঝুঁকিটা বুঝি। TLS 1.3-এর পূর্ণ handshake-এ সার্ভার প্রতিবার নিজের নতুন এলোমেলো মান দেয়, তাই পুরোনো রেকর্ড করা বার্তা আবার পাঠালে কাজ করে না। 0-RTT-তে ক্লায়েন্ট সার্ভারের উত্তরের আগেই আগের session-এর key দিয়ে ডেটা পাঠায় — সার্ভারের কোনো নতুন অবদান নেই। ফলে পথের কেউ সেই datagram কপি করে পরে আবার পাঠালে সার্ভার সেটিকে বৈধ মনে করতে পারে। TLS 1.3-এর মান (RFC 8446) নিজেই বলে, 0-RTT ডেটা replay-র বিরুদ্ধে সুরক্ষিত নয়, আর এর forward secrecy-ও পূর্ণ handshake-এর মতো শক্ত নয়। লক্ষ করুন, আক্রমণকারী বিষয়বস্তু পড়তে বা বদলাতে পারে না; শুধু একই অনুরোধ আবার ঘটাতে পারে।

ব্যাংকের সার্ভারে নীতি তাই স্তরে স্তরে: (১) 0-RTT-তে শুধু idempotent, অবস্থা-না-বদলানো অনুরোধ — যেমন স্থির ছবি, CSS, প্রকাশ্য বিনিময় হার। টাকা পাঠানো, লগইন, পাসওয়ার্ড বদলানো কখনো নয়। (২) সার্ভারের দিকে anti-replay কৌশল: ticket একবারই ব্যবহারযোগ্য রাখা, বা ClientHello-র একটি রেকর্ড রেখে একই বার্তা দুবার এলে প্রত্যাখ্যান, আর সময়সীমা বেঁধে দেওয়া — RFC 8446 এগুলোর বর্ণনা দেয়, তবে বহু সার্ভারের বিতরণ-ব্যবস্থায় নিখুঁত single-use কঠিন। (৩) অ্যাপ্লিকেশন স্তরে আলাদা প্রতিরক্ষা: প্রতিটি লেনদেনে একটি অনন্য idempotency key, যাতে একই অনুরোধ দুবার এলেও লেনদেন একবারই হয় — এটি এমনিতেই ভালো নকশা, কারণ নেটওয়ার্ক ছিঁড়ে গিয়ে ব্যবহারকারী নিজেও “আবার চেষ্টা” চাপতে পারেন। (৪) HTTP স্তরে RFC 8470-এর নিয়ম: CDN বা proxy অনুরোধটিকে early data হিসেবে চিহ্নিত করে, আর মূল সার্ভার সন্দেহ হলে 425 Too Early দেয়।

অনেক প্রতিষ্ঠান সরল পথ বেছে নেয়: সংবেদনশীল domain-এ 0-RTT একদম বন্ধ, আর কনটেন্টের domain-এ চালু। এক RTT-র লাভ সত্যিকারের, কিন্তু টাকার লেনদেনে একবারের দ্বিগুণ কাটা তার চেয়ে অনেক বড় ক্ষতি।

মূল উপলব্ধি: গতির প্রতিটি কৌশলের একটি নিরাপত্তা-শর্ত থাকে। 0-RTT নিরাপদ তখনই, যখন অনুরোধটি দুবার ঘটলেও কিছু বদলায় না — আর সেই নিশ্চয়তা transport নয়, অ্যাপ্লিকেশনকেই দিতে হয়।

প্র ০৪SCTP-তে QUIC-এর অনেক ধারণা (একাধিক stream, multi-homing) প্রায় দুই দশক আগে ছিল। তবু SCTP সাধারণ ইন্টারনেটে ব্যর্থ হলো, QUIC সফল — কেন?

SCTP তৈরি হয়েছিল টেলিফোন নেটওয়ার্কের signaling বার্তা IP-র ওপর বহন করার জন্য, আর মান হয় ২০০০ সালে (RFC 2960; পরে RFC 4960, আর এখন RFC 9260). এর নকশা ছিল সত্যিই এগিয়ে: একটি association-এ একাধিক স্বাধীন stream — যাতে এক stream-এর ক্ষতি অন্যকে আটকায় না, ঠিক QUIC-এর মতো — বার্তার সীমানা রক্ষা, আর multi-homing: একটি সংযোগ একাধিক IP ঠিকানা জানে, একটি পথ গেলে অন্যটিতে চলে যায়। চার-ধাপের handshake-এ cookie দিয়ে SYN flood-এর মতো আক্রমণও ঠেকানো হতো।

তবু সাধারণ ইন্টারনেটে SCTP প্রায় অচল, কারণ এটি IP header-এ নিজের protocol নম্বর (132) নিয়ে একটি নতুন transport. বাসার NAT জানে না SCTP-র port কোথায়, তাই translate করতে পারে না; অনেক firewall অচেনা protocol সরাসরি ফেলে দেয়। Windows-এ বা মোবাইলে kernel-এর সমর্থন ছিল না বা দুর্বল। ফলে একটি “ডিম আগে না মুরগি আগে” সমস্যা: নেটওয়ার্ক সমর্থন করে না বলে অ্যাপ ব্যবহার করে না, অ্যাপ ব্যবহার করে না বলে নেটওয়ার্ক সমর্থন যোগ করে না। SCTP তাই টিকে আছে নিয়ন্ত্রিত পরিবেশে — মোবাইল অপারেটরের নিজস্ব নেটওয়ার্কের ভেতরে, যেখানে সব যন্ত্র একই প্রতিষ্ঠানের — আর মজার একটি জায়গায়: WebRTC-র data channel SCTP ব্যবহার করে, কিন্তু DTLS আর UDP-র ভেতরে মুড়িয়ে, অর্থাৎ ঠিক QUIC-এর কৌশলে।

QUIC এই শিক্ষা পুরোপুরি নিয়েছে। নতুন protocol নম্বর নয়, UDP — যা সবাই পার করে। Kernel নয়, অ্যাপ — যা দ্রুত হালনাগাদ হয়। খোলা header নয়, এনক্রিপশন — যাতে middlebox আবার আটকাতে না পারে। আর সবচেয়ে বড় কথা, শুরু থেকে একটি বড় চাহিদা আর দুই প্রান্তের বড় খেলোয়াড় (ব্রাউজার ও বড় সার্ভার) — ফলে প্রথম দিন থেকেই বাস্তব ট্রাফিকে লাভ দেখানো গেছে।

মূল উপলব্ধি: ইন্টারনেটে একটি protocol-এর সাফল্য শুধু তার নকশার ভালোত্বে নয়, চালু হওয়ার পথে। QUIC-এর ধারণা নতুন নয়; নতুন হলো আজকের জমে যাওয়া ইন্টারনেটে সেগুলো পৌঁছে দেওয়ার কৌশল।

প্র ০৫QUIC কি একদিন TCP-কে পুরোপুরি সরিয়ে দেবে? Data center, SSH, database বা IoT-র মতো জায়গার কথা ভেবে উত্তর দিন।

QUIC-এর সুবিধাগুলো সবচেয়ে বেশি কাজে লাগে যেখানে RTT বড়, loss আছে, ক্লায়েন্ট নড়াচড়া করে, আর একটি সংযোগে অনেক স্বাধীন অনুরোধ — অর্থাৎ ইন্টারনেটের ওপর দিয়ে ব্রাউজার আর মোবাইল অ্যাপ। এখানে QUIC দ্রুত ছড়াচ্ছে, আর DNS-ও (DNS over QUIC, RFC 9250) একই পথে এসেছে।

কিন্তু অনেক জায়গায় হিসাব ভিন্ন। Data center-এর ভেতরে RTT মাইক্রোসেকেন্ডে, loss প্রায় শূন্য, আর সবচেয়ে বড় খরচ CPU — এখানে kernel-এর TCP (TSO, GRO, zero-copy-সহ) বা এমনকি RDMA-র মতো kernel এড়ানো প্রযুক্তি অনেক সময় বেশি দক্ষ। দীর্ঘস্থায়ী একক সংযোগে (SSH, database-এর replication, বড় ফাইল কপি) handshake-এর এক-দুই RTT বাঁচানোর গুরুত্ব কম, আর head-of-line blocking-এর প্রশ্নও ওঠে না, কারণ সেখানে একটিই stream. ছোট IoT যন্ত্রে TLS 1.3 আর QUIC-এর পুরো বাস্তবায়ন memory-তে কুলাতে কষ্ট হতে পারে, যদিও হালকা বাস্তবায়ন নিয়ে কাজ চলছে। আর প্রতিষ্ঠানের নেটওয়ার্কে UDP আটকানো থাকলে fallback হিসেবে TCP লাগবেই।

সম্ভাব্য ভবিষ্যৎ তাই প্রতিস্থাপন নয়, বিভাজন: ইন্টারনেটের দিকে মুখ-করা ট্রাফিক মূলত QUIC-এ, ভেতরের আর দীর্ঘ সংযোগে TCP, আর দুটির মাঝে proxy আর CDN অনুবাদক।

মূল উপলব্ধি: Transport বেছে নেওয়া একটি প্রকৌশল-সিদ্ধান্ত: RTT, loss, গতিশীলতা, CPU আর পরিচালনার সুবিধা মিলিয়ে। QUIC আধুনিক ওয়েবের জন্য ভালো উত্তর — কিন্তু “সবকিছুর” উত্তর এখনো নয়।

অনুশীলন

খাতা-কলম নিন — RTT, বাইট আর bit ধরে ধরে হিসাব করুন।

  1. হিসাব (latency আর ৩ গুণ সীমা): (ক) ঢাকা থেকে ইউরোপের একটি সার্ভারে RTT ১২০ মিলিসেকেন্ড ধরে নিন। নতুন সংযোগে প্রথম বাইট পর্যন্ত সময় বের করুন: TCP + TLS 1.2, TCP + TLS 1.3, QUIC, QUIC 0-RTT. (খ) একটি QUIC ক্লায়েন্ট প্রথমে একটিমাত্র ১২০০ বাইটের Initial datagram পাঠাল। সার্ভারের প্রথম উত্তরের (certificate-সহ) মোট দরকার ৪,৮০০ বাইট। ঠিকানা যাচাইয়ের আগে সার্ভার এখনই কত বাইট পাঠাতে পারবে, আর বাকিটার কী হবে?

    (ক) সূত্র: (setup RTT + ১) × RTT. TCP + TLS 1.2 = ৪ × ১২০ = ৪৮০ ms; TCP + TLS 1.3 = ৩ × ১২০ = ৩৬০ ms; QUIC = ২ × ১২০ = ২৪০ ms; QUIC 0-RTT = ১ × ১২০ = ১২০ ms. পুরোনো পথের তুলনায় নতুন সংযোগে QUIC বাঁচায় ২৪০ ms — প্রায় এক-চতুর্থাংশ সেকেন্ড, প্রতিটি নতুন সার্ভারে।

    (খ) সীমা = ৩ × ১২০০ = ৩,৬০০ বাইট. বাকি ৪,৮০০ − ৩,৬০০ = ১,২০০ বাইট সার্ভার আটকে রাখবে, যতক্ষণ না ক্লায়েন্ট থেকে আরও বাইট আসে (যেমন ক্লায়েন্টের পরের ACK-বাহী datagram; ক্লায়েন্ট আরেকটি ১২০০ বাইটের datagram পাঠালে মোট সীমা হয় ৩ × ২৪০০ = ৭,২০০) বা ক্লায়েন্টের Handshake packet ঠিকানা যাচাই করে। দাম: প্রায় এক RTT বাড়তি দেরি। এজন্যই বড় certificate chain QUIC-এ ক্ষতিকর, আর certificate compression (RFC 8879) বা ছোট chain রাখা কাজে লাগে।

  2. হাতে trace (ACK range আর RTT): (ক) একটি QUIC প্রাপক packet number 0, 1, 2, 3, 4, 5, 7, 8, 9 পেয়েছে, 6 পায়নি। ACK frame-এর Largest Acknowledged, First ACK Range, Gap আর ACK Range Length কী হবে? (খ) প্রেরক packet 7 পাঠিয়েছিল ১০০০ ms-এ, সেটিকে largest acked হিসেবে ধরে ACK পেল ১০৯৫ ms-এ, ACK delay ১৫ ms, min_rtt ৭০ ms, আগের smoothed_rtt ৮৪ ms. নতুন smoothed_rtt কত?

    (ক) সবচেয়ে বড় নম্বর Largest = 9. তার আগে পরপর পাওয়া 8 আর 7, তাই First ACK Range = 2 (প্রথম range-এর সবচেয়ে ছোট = ৯ − ২ = ৭)। তারপর ফাঁক শুধু 6 — একটিমাত্র packet; সূত্র “পরের range-এর সবচেয়ে বড় = ৭ − Gap − ২” থেকে ৫ পেতে Gap = 0. দ্বিতীয় range 5 থেকে 0, তাই ACK Range Length = 5 (সবচেয়ে ছোট = ৫ − ৫ = ০)। ACK Range Count = 1. লক্ষ করুন, Gap-এর মান “ফাঁকের packet সংখ্যা − ১”, তাই একটি হারানো packet-এ Gap ০.

    (খ) latest_rtt = ১০৯৫ − ১০০০ = ৯৫. যেহেতু ৯৫ ≥ ৭০ + ১৫ = ৮৫, adjusted = ৯৫ − ১৫ = ৮০. smoothed = ⅞ × ৮৪ + ⅛ × ৮০ = ৭৩.৫ + ১০ = ৮৩.৫ ms. যদি ack delay বাদ না দেওয়া হতো, ৯৫-এর নমুনা smoothed-কে ভুলভাবে ওপরে টানত। আর packet 7 যদি আগে হারানো কোনো ডেটার পুনঃপাঠানো হয়, তবুও নমুনাটি বৈধ — কারণ নম্বর 7 শুধু একবারই পাঠানো হয়েছে।

  3. আউটপুট অনুমান করুন: অংশ ২-এর head-of-line simulator-এ শুধু LOST = "A2" বদলে LOST = "B1" করা হলো। শেষ তিনটি লাইন (প্রতিটি stream কখন সম্পূর্ণ) কী ছাপবে?
    stream A complete:  TCP 130 ms   QUIC 100 ms
    stream B complete:  TCP 130 ms   QUIC 130 ms
    stream C complete:  TCP 130 ms   QUIC 120 ms

    B1 মূলত পৌঁছাত ৫০ ms-এ; এক RTT পরে ১৩০ ms. TCP-তে তার পরের সব packet ১৩০ পর্যন্ত আটকে থাকে — তিন stream-ই ১৩০-এ শেষ, এমনকি যে stream A-র কিছুই হারায়নি সেটিও। QUIC-এ A ১০০-তে, C ১২০-তে শেষ; শুধু B অপেক্ষা করে ১৩০ পর্যন্ত। হারানো packet যত সামনের দিকে, TCP-তে তত বেশি ডেটা আটকায়।

  4. কোড আর হাতে encode: (ক) হাতে QUIC varint-এ লিখুন: ৩০০ আর ৭০,০০০. (খ) Stream ID 6 আর 13 কোন ধরনের stream, আর সেই ধরনের কত নম্বর? (গ) অংশ ৫-এর encode() ফাংশন ২৬২ বা তার বেশি মান পেলে কী করে? এক লাইনের একটি পরীক্ষা যোগ করুন যাতে ভুল মানে ত্রুটি দেয়।

    (ক) ৩০০ ≥ ৬৪ কিন্তু < ১৬,৩৮৪, তাই ২ বাইট, prefix 01. ৩০০ = 0x012c; ওপরের দুই bit-এ 01 বসাতে 0x4000 যোগ (OR): 0x412c. ৭০,০০০ ≥ ১৬,৩৮৪, তাই ৪ বাইট, prefix 10. ৭০,০০০ = 0x00011170; 0x80000000 OR করে 0x80011170. যাচাই: 0x412c & 0x3fff = ৩০০, 0x80011170 & 0x3fffffff = ৭০,০০০.

    (খ) 6 = বাইনারি 110, নিচের দুই bit 10: bit 0 = 0 → ক্লায়েন্ট খুলেছে, bit 1 = 1 → এক-দিকের; 6 >> 2 = 1, তাই ক্লায়েন্টের এক-দিকের stream-এর ১ নম্বর (০ নম্বর হলো 2). 13 = বাইনারি 1101, নিচের দুই bit 01: সার্ভার খুলেছে, দুই-দিকের; 13 >> 2 = 3, তাই সার্ভারের দুই-দিকের stream-এর ৩ নম্বর (1, 5, 9, 13).

    function encode(v) {
      v = BigInt(v);
      if (v < 0n || v >= 1n << 62n) throw new RangeError("varint must be 0 .. 2^62-1");
      // ... বাকিটা আগের মতোই

    (গ) পরীক্ষা ছাড়া ২৬২-এর মান ৮ বাইটের শাখায় পড়ে, আর ওপরের দুই bit-এ prefix 11 OR হওয়ায় মানের আসল ওপরের bit হারিয়ে যায় — নিঃশব্দে ভুল সংখ্যা তৈরি হয়। এমন নিঃশব্দ ভুল protocol-কোডে সবচেয়ে বিপজ্জনক; তাই সীমা পরীক্ষা করে স্পষ্ট ত্রুটি দেওয়াই নিয়ম। RFC-ও বলে ২৬২ − ১-এর বড় মান varint-এ লেখা যায় না।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি নতুন QUIC সংযোগে ক্লায়েন্ট প্রথম HTTP/3 উত্তর পাচ্ছে — ঘটনাগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. ক্লায়েন্ট ClientHello-সহ Initial packet পাঠায়, datagram অন্তত ১২০০ বাইট
  2. সার্ভার Initial-এ ServerHello আর Handshake packet-এ certificate ও Finished পাঠায়
  3. ক্লায়েন্ট certificate যাচাই করে Finished পাঠায়, একই datagram-এ 1-RTT packet-এ অনুরোধ
  4. সার্ভার অনুরোধ পেয়ে STREAM frame-এ উত্তর আর HANDSHAKE_DONE পাঠায়
  5. ক্লায়েন্ট উত্তরের প্রথম বাইট পায় — শুরু থেকে প্রায় ২ RTT পরে

QUIC-এ stream ID 7 (বাইনারি 111) কোন ধরনের stream নির্দেশ করে?

  • ক্লায়েন্টের খোলা দুই-দিকের stream
  • ক্লায়েন্টের খোলা এক-দিকের stream
  • সার্ভারের খোলা দুই-দিকের stream
  • সার্ভারের খোলা এক-দিকের stream

একটি HTTP/2 সংযোগে (একটি TCP সংযোগ) তিনটি ফাইল তিনটি stream-এ একসাথে আসছে। stream A-র ডেটাবাহী একটি TCP segment হারাল, কিন্তু তার পরের সব segment পৌঁছেছে। হারানো segment-এর পুনঃপাঠানো পৌঁছানোর আগে ব্রাউজারের অবস্থা কী?

  • শুধু stream A আটকে থাকে, B আর C-র ডেটা ব্রাউজার পেতে থাকে
  • তিনটি stream-এর নতুন ডেটাই আটকে থাকে, কারণ TCP ক্রম মেনে বাইট দেয়
  • TCP সংযোগটি ভেঙে যায় আর নতুন handshake শুরু হয়
  • ব্রাউজার ফাঁকসহ ডেটা পায় আর নিজে A-র অংশ বাদ দেয়

একটি QUIC সার্ভার একটি নতুন, অযাচাইকৃত ঠিকানা থেকে মোট ২,৪০০ বাইট পেয়েছে। ঠিকানা যাচাইয়ের আগে সেই ঠিকানায় সে সর্বোচ্চ কত বাইট পাঠাতে পারবে?

  • ১,২০০ বাইট
  • ২,৪০০ বাইট
  • ৭,২০০ বাইট
  • কোনো সীমা নেই, congestion window যতটা দেয়

একটি অ্যাপ QUIC 0-RTT চালু করতে চায়। নিচের কোন অনুরোধটি 0-RTT ডেটায় পাঠানো সবচেয়ে নিরাপদ?

  • একটি পণ্যের স্থির ছবি আনার GET অনুরোধ
  • ১,০০০ টাকা পাঠানোর POST অনুরোধ
  • পাসওয়ার্ড বদলানোর অনুরোধ
  • একটি নতুন অর্ডার দেওয়ার অনুরোধ

একটি ফোন ডাউনলোডের মাঝখানে Wi-Fi ছেড়ে মোবাইল ডেটায় গেল, ফলে তার উৎস IP আর port বদলাল। QUIC সার্ভার কীভাবে বোঝে এটি একই সংযোগ?

  • উৎস আর গন্তব্যের IP ও port (4-tuple) মিলিয়ে
  • Packet-এর Destination Connection ID দেখে, তারপর PATH_CHALLENGE দিয়ে নতুন পথ যাচাই করে
  • ফোনের MAC ঠিকানা দেখে
  • নতুন একটি পূর্ণ TLS handshake করে

QUIC variable-length integer-এর দুটি বাইট 7b bd-এর মান কত?

  • ৩১৬৭৭
  • ৫৯
  • ১৫২৯৩
  • ১২৩

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. TCP-র চার সমস্যা: handshake-এর RTT (TLS 1.2-সহ প্রথম বাইট ৪ RTT, TLS 1.3-সহ ৩), stream-এর মাঝে head-of-line blocking, middlebox আর kernel-এর কারণে ossification, আর 4-tuple-বাঁধা সংযোগ।
  2. QUIC = UDP + নির্ভরযোগ্যতা + TLS 1.3 + stream, user space-এ; RFC 9000/9001/9002 (মে ২০২১), HTTP/3 RFC 9114. নতুন সংযোগে ২ RTT, 0-RTT-তে ১ — 0-RTT শুধু idempotent অনুরোধে।
  3. Header প্রায় পুরো এনক্রিপ্টেড, packet-এ frame (STREAM, ACK, CRYPTO, MAX_DATA, MAX_STREAM_DATA…), সংখ্যা varint-এ (প্রথম দুই bit = দৈর্ঘ্য), stream ID-র bit 0 = কে খুলেছে, bit 1 = দিক।
  4. Packet number কখনো ফেরে না — retransmission-এর পরও RTT নির্ভুল (Karn-এর সমস্যা নেই); ACK-এ range আর ack delay; congestion control বদলযোগ্য (RFC 9002-এ NewReno-ধাঁচ, বাস্তবে CUBIC/BBR-ও)।
  5. Connection ID দিয়ে migration, PATH_CHALLENGE/PATH_RESPONSE দিয়ে পথ যাচাই; যাচাইয়ের আগে ৩ গুণ সীমা, Initial অন্তত ১২০০ বাইট, চাপের সময় Retry token. UDP আটকালে TCP-তে fallback.

পরের পর্বে: transport স্তর শেষ, এবার তার ওপরের স্তর — application layer. Client-server আর peer-to-peer নকশা, একটি application protocol কীভাবে বানানো হয়, আর কোন অ্যাপ কোন transport বেছে নেয় — TCP, UDP নাকি QUIC.

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

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান