পাঠ ৩৪ · ৫৭-এর মধ্যে · মডিউল ৭
Home / Courses / Computer Networks / সকেট প্রোগ্রামিং

সকেট প্রোগ্রামিং পরিচিতি

Socket programming intro
৮ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • সকেট আসলে কী এবং এটি অ্যাপ্লিকেশন ও ট্রান্সপোর্ট লেয়ারের মধ্যে কীভাবে সেতু তৈরি করে
  • Python-এর real socket মডিউলের মূল API — bind, listen, accept, connect, send, recv
  • কেন এই কোর্সের কোনো কোড সেল real সকেট খোলে না, এবং কীভাবে একটি MockSocket সেই আচরণ নিরাপদে সিমুলেট করে
  • একটি ইন-মেমরি ক্লায়েন্ট-সার্ভার বিনিময়ের সিমুলেশন যা real API-এর প্রতিটি ধাপ অনুসরণ করে

১ · সকেট কী এবং কেন প্রয়োজন

সকেটSocketঅ্যাপ্লিকেশন কোড ও অপারেটিং সিস্টেমের নেটওয়ার্কিং মেশিনারির মধ্যেকার প্রোগ্রামিং ইন্টারফেস — একটি এন্ডপয়েন্ট যার মাধ্যমে ডেটা পাঠানো/গ্রহণ করা হয়। হলো সেই ইন্টারফেস যা দিয়ে একটি অ্যাপ্লিকেশন OS-এর ট্রান্সপোর্ট-লেয়ার (TCP/UDP, M5) মেশিনারির সাথে কথা বলে। M6-এ আমরা যে প্রোটোকলগুলো দেখেছি — HTTP, DNS, SMTP, FTP — এই সবই বাস্তবে সকেট ব্যবহার করে বাস্তবায়িত হয়। M1/L01-এর লেয়ারিং নীতির সরাসরি প্রয়োগ হিসেবে ভাবুন — অ্যাপ্লিকেশন লেয়ার কোড শুধু সকেট API-এর সাথে কথা বলে, নিচে IP রাউটিং বা ফিজিক্যাল মিডিয়ার খুঁটিনাটি নিয়ে মাথা ঘামাতে হয় না।

২ · Python-এর real সকেট API

নিচের কোডটি real Python কোড — এটি আপনার নিজের কম্পিউটারে সরাসরি চালানো যায় (এই পাঠের স্যান্ডবক্সে নয়, নিচে ব্যাখ্যা করা হয়েছে) —

socket.socket(...)
socket.socket(socket.AF_INET, socket.SOCK_STREAM) — একটি TCP সকেট তৈরি করে; SOCK_DGRAM দিলে UDP সকেট তৈরি হয়।
.bind((host, port))
সার্ভার-পাশে — সকেটটিকে একটি নির্দিষ্ট ঠিকানা ও পোর্টে "বেঁধে" দেয়, যাতে ইনকামিং ট্রাফিক সেখানে পৌঁছাতে পারে।
.listen()
সার্ভার-পাশে — সকেটকে ইনকামিং কানেকশন গ্রহণের জন্য প্রস্তুত করে (TCP-নির্দিষ্ট, M5/L24-এর হ্যান্ডশেকের আগে)।
.accept()
সার্ভার-পাশে — একটি ইনকামিং কানেকশন গ্রহণ করে (M7/L35-এ বিস্তারিত দেখব এটি প্রতিটি ক্লায়েন্টের জন্য নতুন সকেট ফেরত দেয়)।
.connect((host, port))
ক্লায়েন্ট-পাশে — একটি নির্দিষ্ট সার্ভার ঠিকানায় সংযোগ স্থাপনের অনুরোধ পাঠায়।
.send() / .recv()
একবার সংযুক্ত হলে — সংযোগের দুই পাশই এই মেথড দিয়ে ডেটা পাঠায়/গ্রহণ করে।
গুরুত্বপূর্ণ — কেন এই পাঠের কোড সেল real সকেট ব্যবহার করে না

এই কোর্সের কোড সেলগুলো একটি ব্রাউজার-ভিত্তিক পাইথন স্যান্ডবক্সে (Pyodide) চলে, যেখানে real নেটওয়ার্ক সকেট খোলা সম্ভব নয় — socket.socket(), .bind() বা .connect() real কল করার চেষ্টা করলে তা হ্যাং করবে বা এরর দেবে, কারণ ব্রাউজার স্যান্ডবক্স কোনো real পোর্ট বাইন্ড করতে দেয় না। তাই নিচের কোড সেলটি একটি ইচ্ছাকৃত সিমুলেশন — একটি MockSocket ক্লাস, যার মেথডের নাম ও গঠন real API-এর মতো (bind, listen, connect, send, recv), কিন্তু ভেতরে শুধু in-memory তালিকা (list) দিয়ে কাজ করে — এটি real সকেটের কোনো কার্যকরী বিকল্প নয়, শুধু এর আচরণ নিরাপদে বোঝার একটি উপায়।

সার্ভার: bind() → listen() → accept() ক্লায়েন্ট: connect() সার্ভার: recv() ← ক্লায়েন্টের ডেটা ক্লায়েন্ট: send() → ডেটা পাঠায় সার্ভার: send() → রেসপন্স পাঠায় ক্লায়েন্ট: recv() ← রেসপন্স গ্রহণ
সার্ভার আগে bind/listen/accept দিয়ে প্রস্তুত হয়, তারপর ক্লায়েন্ট connect করে — সংযোগ প্রতিষ্ঠার পর উভয় পাশ send/recv দিয়ে ডেটা আদান-প্রদান করে।
Python
# *** সিমুলেশন — কোনো real নেটওয়ার্ক সকেট খোলা হচ্ছে না ***
# real socket.socket()/.bind()/.connect() Pyodide-এ কাজ করবে না বলে
# এখানে একই মেথড-নামের একটি in-memory MockSocket ব্যবহার করা হয়েছে।

class MockSocket:
    def __init__(self, name):
        self.name = name
        self.address = None
        self.send_queue = None   # এই সকেট যে কিউতে ডেটা লেখে
        self.recv_queue = None   # এই সকেট যে কিউ থেকে ডেটা পড়ে

    def bind(self, address):
        self.address = address
        print(f"[{self.name}] bind({address}) — (সিমুলেটেড) এই ঠিকানায় শোনা শুরু")

    def listen(self):
        print(f"[{self.name}] listen() — ইনকামিং কানেকশনের জন্য প্রস্তুত")

    def connect(self, server_socket):
        # দুইটি ওয়ান-ওয়ে কিউ তৈরি করে দ্বিমুখী যোগাযোগ সিমুলেট করা হচ্ছে
        client_to_server = []
        server_to_client = []
        self.send_queue, self.recv_queue = client_to_server, server_to_client
        server_socket.send_queue, server_socket.recv_queue = server_to_client, client_to_server
        print(f"[{self.name}] connect() -> {server_socket.name} (সিমুলেটেড হ্যান্ডশেক)")

    def accept(self):
        print(f"[{self.name}] accept() — ইনকামিং কানেকশন গ্রহণ করা হলো")

    def send(self, data):
        self.send_queue.append(data)
        print(f"[{self.name}] send({data!r})")

    def recv(self):
        data = self.recv_queue.pop(0)
        print(f"[{self.name}] recv() -> {data!r}")
        return data


server = MockSocket("server")
client = MockSocket("client")

server.bind(("0.0.0.0", 9000))
server.listen()

client.connect(server)
server.accept()

client.send("GET /hello")
received = server.recv()

server.send("HTTP/1.1 200 OK")
reply = client.recv()

print("\nক্লায়েন্ট যা পেল:", reply)

    
লক্ষ্য করুন — MockSocket-এর প্রতিটি মেথডের নাম (bind, listen, connect, accept, send, recv) হুবহু real Python socket মডিউলের সাথে মিলে যায় — শুধু ভেতরের বাস্তবায়ন real নেটওয়ার্ক I/O-এর বদলে একটি সাধারণ পাইথন list। এই কাঠামোটাই M7/L35-L36-এ TCP ও UDP ক্লায়েন্ট-সার্ভার প্রোগ্রামিং-এর ভিত্তি হবে।
মূল কথা · Key takeaway

সকেট হলো নেটওয়ার্ক প্রোগ্রামিং-এর মৌলিক বিল্ডিং ব্লক — বাস্তব জগতে bind/listen/ accept/connect/send/recv এই মেথডগুলোই ব্যবহার হয়, আর এই পাঠে দেখানো MockSocket তাদের আচরণ নিরাপদে, real নেটওয়ার্ক I/O ছাড়াই শিখিয়েছে।

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

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

প্র ০১ এই পাঠের কোড সেল কেন real socket.socket() কল করে না?

এই কোর্সের কোড ব্রাউজারে চলা Pyodide নামক একটি স্যান্ডবক্সড পাইথন এনভায়রনমেন্টে এক্সিকিউট হয় — নিরাপত্তার কারণে ব্রাউজার স্যান্ডবক্স কোনো real নেটওয়ার্ক পোর্ট বাইন্ড বা কানেক্ট করতে দেয় না। real socket.socket() কল করার চেষ্টা করলে তা এরর দেবে বা হ্যাং করবে। তাই আমরা একই মেথড-নামের একটি MockSocket ব্যবহার করি, যা real API-এর আচরণ বোঝায় কিন্তু আসলে শুধু in-memory ডেটা নিয়ে কাজ করে।

প্র ০২ সার্ভার-পাশে .listen() কল করার পরেও কেন .accept() আলাদা করে কল করতে হয়?

.listen() শুধু সকেটটিকে "প্রস্তুত" করে (OS-কে বলে দেয় এই পোর্টে ইনকামিং কানেকশন রিকোয়েস্ট কিউতে জমা রাখতে), কিন্তু কোনো একটি নির্দিষ্ট ক্লায়েন্টের সাথে প্রকৃত সংযোগ স্থাপন করে না। .accept() হলো সেই কল যা কিউ থেকে একটি অপেক্ষমাণ কানেকশন রিকোয়েস্ট তুলে নিয়ে সেই নির্দিষ্ট ক্লায়েন্টের সাথে সংযোগ সম্পূর্ণ করে — M7/L35-এ আমরা দেখব এটি আসলে একটি নতুন, ডেডিকেটেড সকেট অবজেক্টও ফেরত দেয়।

প্র ০৩ M6-এর প্রোটোকলগুলো (HTTP, DNS, SMTP) সবাই একই সকেট API ব্যবহার করে — তাহলে তাদের আচরণ ভিন্ন হয় কীভাবে?

সকেট শুধু বাইট পাঠানো/গ্রহণের নিচু-স্তরের ইন্টারফেস দেয় — প্রতিটি প্রোটোকল সেই কাঁচা বাইট স্ট্রিমের উপর নিজস্ব নিয়ম আরোপ করে (কোন ফরম্যাটে রিকোয়েস্ট লেখা হবে, কোন ক্রমে বার্তা আদান-প্রদান হবে ইত্যাদি)। HTTP "GET /path HTTP/1.1\r\n..." ফরম্যাটে টেক্সট পাঠায়, SMTP অন্য কমান্ড সেট ব্যবহার করে — কিন্তু নিচে সবাই একই send/recv কল করছে। এটিই M1/L01-এর লেয়ারিং নীতির বাস্তব উদাহরণ।

অনুশীলন

  1. চিন্তা করুন: real জীবনে একটি ওয়েব সার্ভার (যেমন একটি ওয়েবসাইট হোস্ট করা) একসাথে হাজার হাজার ব্যবহারকারীর রিকোয়েস্ট সামলায় — এটি কি একটিমাত্র সকেট দিয়ে সম্ভব?

    না — সার্ভারের মূল listening সকেটটি শুধু নতুন কানেকশন গ্রহণ করে; প্রতিটি .accept() কল সেই নির্দিষ্ট ক্লায়েন্টের জন্য একটি সম্পূর্ণ নতুন, ডেডিকেটেড সকেট তৈরি করে (M7/L35-এ এটি বিস্তারিত দেখব)। তাই হাজার হাজার ব্যবহারকারীর মানে হাজার হাজার আলাদা কানেকশন সকেট, যদিও তারা সবাই একই মূল listening পোর্টের মাধ্যমে সংযুক্ত হয়েছিল।

  2. পরীক্ষা করুন: উপরের কোড সেলে client.send(...)-এর বার্তা পরিবর্তন করে এবং server.send(...)-এ ভিন্ন একটি রেসপন্স বসিয়ে আবার Run করুন — আউটপুট প্রত্যাশিতভাবে বদলায় কি না দেখুন।

    হ্যাঁ — যেহেতু MockSocket.send() শুধু দেওয়া ডেটাটিকে হুবহু কিউতে যোগ করে এবং recv() সেটিকে হুবহু ফেরত দেয়, আপনি যা পাঠাবেন ঠিক তাই অন্য পাশে পৌঁছাবে — এটিই দেখায় সকেট নিজে ডেটার কনটেন্ট নিয়ে কিছু বোঝে না, শুধু বাইট পরিবহন করে।

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

আগের পাঠ
DHCP — ডায়নামিক হোস্ট কনফিগারেশন