CoAP প্রোটোকল
এই পাঠে যা শিখবেন
- CoAP কেন UDP বেছে নেয় এবং MQTT-এর TCP-ভিত্তিক মডেলের থেকে কীভাবে আলাদা
- Confirmable (CON) বনাম Non-confirmable (NON) মেসেজের পার্থক্য
- মেসেজ আইডি, ACK, এবং এক্সপোনেনশিয়াল-ব্যাকঅফ রিট্রান্সমিশন কীভাবে কাজ করে — একটি জেনুইন কম্পিউটেড উদাহরণসহ
- CoAP-এর REST-সদৃশ মেথড ও রেসপন্স কোড সম্পর্কে সংক্ষিপ্ত তথ্যভিত্তিক পরিচিতি
১ · কেন UDP, TCP নয়
CoAPConstrained Application Protocolখুবই সীমিত মেমরি/পাওয়ারের মাইক্রোকন্ট্রোলার-শ্রেণির ডিভাইসের জন্য ডিজাইন করা একটি হালকা-ওজনের REST-সদৃশ প্রোটোকল, যা UDP-এর ওপর চলে। এমন ডিভাইসের জন্য ডিজাইন করা হয়েছে যেখানে একটি পূর্ণাঙ্গ TCP কানেকশন (কানেকশন সেটআপ/টিয়ারডাউন, কানেকশন-স্টেট বজায় রাখা) বহন করার মতো মেমরিও নেই। CoAP তাই UDP-এর ওপর চলে — UDP-তে কোনো কানেকশন সেটআপ নেই, প্রতিটি প্যাকেট স্বাধীনভাবে পাঠানো হয় (TCP/UDP-এর সাধারণ পার্থক্য Computer Networks কোর্সে কভার করা হয়েছে)। কিন্তু UDP নিজে থেকে কোনো ডেলিভারি গ্যারান্টি দেয় না — একটি প্যাকেট পথে হারিয়ে যেতে পারে, আর UDP কখনো জানাবে না। CoAP তাই নিজস্ব একটি হালকা রিলায়েবিলিটি মেকানিজম তৈরি করেছে, যা নিচে সিমুলেট করা হয়েছে।
২ · Confirmable মেসেজ, মেসেজ আইডি ও ACK
একটি CONConfirmable messageএকটি CoAP মেসেজ যার জন্য প্রাপকের কাছ থেকে একটি ACK আবশ্যক — না পেলে প্রেরক নির্দিষ্ট নিয়মে পুনরায় পাঠায়। (Confirmable) মেসেজে একটি ইউনিক Message ID থাকে। রিসিভার সেই একই Message ID-সহ একটি ACK ফেরত পাঠায় (সাধারণত রেসপন্সটাই "পিগিব্যাক" করে ACK-এর সাথে একসাথে পাঠানো হয়)। প্রেরক যদি নির্দিষ্ট সময়ের মধ্যে ACK না পায়, সে একই Message ID দিয়ে পুনরায় পাঠায় — এবং প্রতিবার ব্যর্থ হলে অপেক্ষার সময় দ্বিগুণ করে (এক্সপোনেনশিয়াল ব্যাকঅফ), একটি সর্বোচ্চ রিট্রান্সমিশন সংখ্যা পর্যন্ত। এর বিপরীতে একটি Non-confirmable (NON) মেসেজে কোনো ACK আশাই করা হয় না — একেবারে "পাঠিয়ে ভুলে যাও" (fire-and-forget), ঘন ঘন পাঠানো নন-ক্রিটিক্যাল স্ট্রিমিং ডেটার জন্য উপযুক্ত।
৩ · একটি সত্যিকারের রিট্রান্সমিশন সিমুলেশন
নিচের কোডে একটি সরল CoapServer (কিছু রিসোর্স ধারণ করে) এবং একটি
simulate_con_request() ফাংশন আছে যা CON রিকোয়েস্ট পাঠানোর প্রকৃত রিট্রাই লজিক সিমুলেট করে —
কতবার মেসেজ হারালো, কত সেকেন্ড অপেক্ষা করা হলো (ব্যাকঅফ দ্বিগুণ করে), এবং শেষ পর্যন্ত সফল হলো নাকি
সর্বোচ্চ রিট্রান্সমিশন সংখ্যা (RFC 7252-এ সাধারণত বহুল-উল্লেখিত ডিফল্ট MAX_RETRANSMIT = 4)
অতিক্রম করে ব্যর্থ হলো — সবকিছুই কোডের ভেতর থেকে জেনুইনভাবে গণনা করা, কোথাও হার্ডকোড করা ফলাফল নেই।
class CoapServer:
"""একটি সরলীকৃত CoAP সার্ভার -- কিছু রিসোর্স পাথ ধরে রাখে।"""
def __init__(self):
self.resources = {
"/sensors/temperature": 24.5,
"/sensors/humidity": 61,
}
self._next_mid = 1
def new_message_id(self):
mid = self._next_mid
self._next_mid += 1
return mid
def simulate_con_request(server, path, ack_lost_count=0, ack_timeout=2.0, max_retransmit=4):
"""একটি CON GET রিকোয়েস্ট সিমুলেট করে। ack_lost_count = প্রথম কতগুলো
চেষ্টার ACK হারিয়ে যাবে (সিমুলেটেড নেটওয়ার্ক লস)। প্রতিটি লসের পর
টাইমআউট এক্সপোনেনশিয়ালি দ্বিগুণ হয় (RFC 7252-এর ব্যাকঅফ নিয়ম)।"""
mid = server.new_message_id()
log = [f"ক্লায়েন্ট -> সার্ভার: CON GET {path} (Message-ID={mid})"]
timeout = ack_timeout
total_wait = 0.0
for attempt in range(max_retransmit + 1): # attempt 0 = আসল পাঠানো, ১..৪ = রিট্রান্সমিট
if attempt < ack_lost_count:
log.append(f" চেষ্টা #{attempt + 1}: {timeout:.1f}s অপেক্ষার পর কোনো ACK এলো না (হারিয়ে গেছে, সিমুলেটেড)")
total_wait += timeout
timeout *= 2 # এক্সপোনেনশিয়াল ব্যাকঅফ
if attempt < max_retransmit:
log.append(f" ক্লায়েন্ট পুনরায় পাঠালো: CON GET {path} (Message-ID={mid}, চেষ্টা #{attempt + 2})")
continue
value = server.resources.get(path)
if value is not None:
log.append(f" চেষ্টা #{attempt + 1}: ACK 2.05 Content প্রাপ্ত (Message-ID={mid}), payload={value!r}")
return {"success": True, "attempts": attempt + 1, "total_wait_s": total_wait, "log": log}
else:
log.append(f" চেষ্টা #{attempt + 1}: রিসোর্স পাওয়া যায়নি -> 4.04 Not Found (Message-ID={mid})")
return {"success": False, "attempts": attempt + 1, "total_wait_s": total_wait, "log": log}
log.append(f"MAX_RETRANSMIT ({max_retransmit}) অতিক্রান্ত, মোট অপেক্ষা {total_wait:.1f}s -> অনুরোধ ব্যর্থ")
return {"success": False, "attempts": max_retransmit + 1, "total_wait_s": total_wait, "log": log}
server = CoapServer()
print("== দৃশ্য ১: কোনো লস নেই, সাথে সাথে ACK ==")
result1 = simulate_con_request(server, "/sensors/temperature", ack_lost_count=0)
for line in result1["log"]:
print(line)
print(f" ফলাফল: success={result1['success']}, মোট চেষ্টা={result1['attempts']}, মোট অপেক্ষা={result1['total_wait_s']}s")
print("\n== দৃশ্য ২: প্রথম ২টি ACK হারিয়ে যায়, তৃতীয় চেষ্টায় সফল ==")
result2 = simulate_con_request(server, "/sensors/temperature", ack_lost_count=2)
for line in result2["log"]:
print(line)
print(f" ফলাফল: success={result2['success']}, মোট চেষ্টা={result2['attempts']}, মোট অপেক্ষা={result2['total_wait_s']}s")
print("\n== দৃশ্য ৩: সবগুলো চেষ্টাই হারিয়ে যায় -> MAX_RETRANSMIT ছাড়িয়ে ব্যর্থ ==")
result3 = simulate_con_request(server, "/sensors/temperature", ack_lost_count=10)
for line in result3["log"]:
print(line)
print(f" ফলাফল: success={result3['success']}, মোট চেষ্টা={result3['attempts']}, মোট অপেক্ষা={result3['total_wait_s']}s")
total_wait_s জেনুইনভাবে $2 + 4 + 8 + 16 + 32 = 62.0$ সেকেন্ড হিসেবে
বের হয় (৫টি চেষ্টা: প্রথম পাঠানো + ৪টি রিট্রান্সমিট, প্রতিবার আগেরটির দ্বিগুণ অপেক্ষা) — এটি কোডের ভেতরের
লুপ থেকেই বাস্তবে যোগ হয়ে আসছে, কোথাও আগে থেকে হিসাব করে বসিয়ে দেওয়া হয়নি। এটাই দেখায় কেন একটি সত্যিই
দুর্বল সংযোগে CoAP-এর ব্যাকঅফ দ্রুতই দীর্ঘ অপেক্ষায় পরিণত হতে পারে।
৪ · REST-সদৃশ মেথড ও রেসপন্স কোড
CoAP-এর মেথডগুলো HTTP-এর সাথে ইচ্ছাকৃতভাবে মিল রেখে ডিজাইন করা — GET, POST,
PUT, DELETE — এবং রেসপন্স কোডগুলোও HTTP-এর স্ট্যাটাস কোডের অনুরূপ প্যাটার্নে
তৈরি (যেমন উপরের কোডে দেখা 2.05 Content ও 4.04 Not Found, HTTP-এর 2xx/4xx-এর
সাথে ধারণাগতভাবে সমতুল্য)। এই পরিচিতিই CoAP-কে "সীমিত ডিভাইসের জন্য REST" বলা হয় — HTTP-এর অভ্যস্ত
মানসিক মডেলটাই বজায় থাকে, শুধু ট্রান্সপোর্ট ও ফ্রেমিং অনেক হালকা।
CoAP = UDP-এর ওপর REST-সদৃশ মডেল + একটি হালকা নিজস্ব রিলায়েবিলিটি লেয়ার (Message ID + CON/ACK + এক্সপোনেনশিয়াল-ব্যাকঅফ রিট্রান্সমিশন)। MQTT-এর সাথে মূল পার্থক্য: MQTT একটি পার্সিস্টেন্ট TCP কানেকশনে pub/sub মডেল চালায়, CoAP প্রতিটি request-কে স্বাধীন UDP প্যাকেট হিসেবে পাঠায় — L49-এ এই দুটোকে (এবং HTTP-কে) পাশাপাশি রেখে সরাসরি তুলনা করা হবে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
উপরের কোডে যদি ack_lost_count=5 দিয়ে (অর্থাৎ max_retransmit=4-এর
সমান বা বেশি) কল করা হয়, তাহলে ফলাফল দৃশ্য ৩-এর মতোই হবে কি?
হ্যাঁ। লুপটি মোট max_retransmit + 1 = 5টি চেষ্টা করে (attempt 0 থেকে 4)। যেহেতু
ack_lost_count=5 মানে সবগুলো attempt (0,1,2,3,4)-ই "হারিয়ে গেছে" শর্ত পূরণ করে
(attempt < 5 সবসময় সত্য এই রেঞ্জে), তাই লুপ শেষ হয়ে যাবে সফল ACK ছাড়াই এবং একই
"MAX_RETRANSMIT অতিক্রান্ত" ফলাফল আসবে — ack_lost_count-এর যেকোনো মান ৫ বা তার বেশি
হলে একই আচরণ হবে, কারণ সর্বোচ্চ ৫টি চেষ্টার বেশি কোডটি কখনোই করে না।
প্র ০২ Non-confirmable (NON) মেসেজে কোনো Message-ID-ভিত্তিক রিট্রান্সমিশন লজিক নেই কেন সেটি এখনও একটি বৈধ ডিজাইন সিদ্ধান্ত?
কারণ কিছু ডেটার জন্য "সর্বশেষ মান গুরুত্বপূর্ণ, প্রতিটি পুরনো মান নয়" — যেমন একটি সেন্সর প্রতি সেকেন্ডে একটি রিডিং পাঠালে, একটি রিডিং হারিয়ে গেলেও এক সেকেন্ড পরেই পরেরটি আসছে। এই ক্ষেত্রে রিট্রান্সমিশনের অতিরিক্ত নেটওয়ার্ক ও পাওয়ার খরচ করার কোনো লাভ নেই — QoS 0-এর পেছনের যুক্তির মতোই (L47), NON মেসেজও একই নীতিতে "at most once, কম ওভারহেড" বেছে নেয়।
প্র ০৩ CoAP কেন নতুন Message-ID না দিয়ে রিট্রান্সমিশনে একই Message-ID পুনরায় ব্যবহার করে?
যাতে রিসিভার বুঝতে পারে এটি একটি নতুন আলাদা রিকোয়েস্ট নয়, বরং আগের একটি রিকোয়েস্টেরই পুনরাবৃত্তি — যদি রিসিভার আসলে প্রথম কপিটি পেয়েও থাকে (আর শুধু তার ACK-টাই হারিয়ে গিয়ে থাকে), তাহলে সে একই কাজ দ্বিতীয়বার না করে শুধু আবার ACK পাঠিয়ে দিতে পারে — একই Message-ID এই ডুপ্লিকেট-শনাক্তকরণ সম্ভব করে তোলে।
অনুশীলন
-
চিন্তা করুন:
ack_timeout=1.0দিয়ে (২.০-এর বদলে) দৃশ্য ২ (দুটি লস, তৃতীয় চেষ্টায় সফল) আবার চালালেtotal_wait_sকত হবে বলে আপনার ধারণা?ব্যাকঅফ এখনও দ্বিগুণ হারে বাড়বে, শুধু শুরুর মান আলাদা: প্রথম লস $1.0$s, দ্বিতীয় লস $2.0$s — মোট $1.0 + 2.0 = 3.0$ সেকেন্ড। মূল দৃশ্য ২-তে ($2.0 + 4.0 = 6.0$s) এর তুলনায় ঠিক অর্ধেক, কারণ প্রতিটি ব্যাকঅফ ধাপ শুরুর
ack_timeout-এর সমানুপাতিক। -
পরীক্ষা করুন: উপরের কোড সেলে
simulate_con_request-কে"/sensors/pressure"(যাserver.resources-এ নেই) পাথ দিয়ে কল করুন এবং Run চেপে দেখুন কী ফলাফল আসে।যেহেতু
"/sensors/pressure"রিসোর্স ডিকশনারিতে নেই,server.resources.get(path)Noneরিটার্ন করবে, আর কোডটি"4.04 Not Found"লগ করেsuccess=False,attempts=1রিটার্ন করবে — এটি রিট্রান্সমিশন লুপের একটি ভিন্ন শাখা, নেটওয়ার্ক লস নয় বরং সার্ভারের প্রকৃত "এই রিসোর্স নেই" উত্তর।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ মাইক্রোপ্রসেসর আর্কিটেকচার, এমবেডেড C, GPIO, টাইমার/PWM/ADC, সিরিয়াল প্রোটোকল, RTOS, সেন্সর/অ্যাকচুয়েটর, IoT আর্কিটেকচার, ওয়্যারলেস প্রোটোকল, MQTT/CoAP ও IoT সিকিউরিটি — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
- Computer Networks কোর্স সহোদর কোর্স TCP বনাম UDP-এর সাধারণ পার্থক্য সেই কোর্সেই কভার করা হয়েছে — এই পাঠ সরাসরি CoAP-এর UDP-ভিত্তিক ডিজাইন সিদ্ধান্তে যায়।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Design and Analysis of Algorithms ও আরও অনেক কোর্স — সব এক জায়গায়।