পাইথন দিয়ে ওয়েব সার্ভার ডেভলপমেন্ট — পার্ট ১

Credit: Parade (Internet is like a layered cake)

এক ভদ্রলোক চিন্তা করলেন নিজের জন্য একটা বাড়ি বানাবেন। এর জন্য মিস্ত্রিদের খবর দিলেন এবং মিস্ত্রিরা যা যা বললো তা তা এনে দিলেন। মিস্ত্রিরা কাজ করছে, এমন সময় লোকটি গিয়ে একজন মিস্ত্রিকে জিজ্ঞাসা করলো “কি করছো তুমি?”, মিস্ত্রিটা একটু বিরক্ত হয়ে উত্তর দিলো “ইট এর গাথুনি দিচ্ছি

লোকটি সন্তষ্ট না হয়ে আরেকজন মিস্ত্রিকে একই প্রশ্ন করলো, তখন সেই মিস্ত্রি উত্তর দিল “আমি ইটের দেয়াল বানাচ্ছি

আবারো সন্তষ্ট না হয়ে তৃতীয় মিস্ত্রিকে একই প্রশ্ন করলো, সে আকাশের দিকে তাকিয়ে উত্তর দিলো “আমি দালান বানাচ্ছি”। সাথে সাথে লোকটিও আকাশের দিকে তাকালো।

আর ঐদিকে দুজন মিস্ত্রি কথা বলছে, “তুমি যে একটা ইট ভুলভাবে গেথেছো এটাতো পরে সমস্যা হবে”। দ্বিতীয় মিস্ত্রি উত্তর দিলো “হেহ, ঐ লোক জানে নাকি কিভাবে করে ইট গেথে দেয়াল বানিয়ে দালান বানাতে হয়? ধরতেই পারবে না

গল্পের সারমর্ম হলো “যদি আপনি পুরো সিস্টেমটা জানেন তাহলে বুঝতে পারবেন বিভিন্ন অংশ(ইট, দেয়াল, দালান) কিভাবে একসাথে কাজ করে এবং কোনো ভুল হলে ধরতে পারবেন এবং ফিক্সও করতে পারবেন

আপনি বিভিন্ন ওয়েব ফ্রেমওয়ার্ক দিয়ে ওয়েব ডেভলপমেন্ট করেন। কিন্ত আপনি কি জানেন এসব ফ্রেমওয়ার্ক কিভাবে ইন্টারনালি কাজ করে? আপনি কি জানেন কিভাবে করে একটা ওয়েব সার্ভার বানাতে হয়?

আমার মনে হয়, আপনি যদি খুব ভালো একজন ডেভলপার হতে চান, তাহলে আপনাকে আরো গভীরে নামতে হবে। জানতে হবে প্রোগ্রামিং ল্যাঙ্গুয়েজ, কম্পাইলার, ইন্টারপ্রেটার, ডাটাবেজ সিস্টেম, অপারেটিং সিস্টেম, ওয়েব সার্ভার, ওয়েব ফ্রেমওয়ার্ক কিভাবে কাজ করে। আর এগুলি সম্পর্কে ভালভাবে জানতে হলে আপনাকে নিজেই এগুলি বানাতে হবে একদম শুন্য থেকে, ব্রিক বাই ব্রিক, ওয়াল বাই ওয়াল। তবেই না দালান বানাতে পারবেন।

আমার এই সিরিজটি ওয়েব সার্ভার নিয়ে। কিভাবে একটা ওয়েব সার্ভার কাজ করে তা একদম শুন্য থেকে দেখাবো কয়েকটা পার্টে।

পূর্বশর্তঃ পাইথনের ইন্টারমিডিয়েট ধারণা, নেটওয়ার্কিং সম্পর্কে ধারণা

প্রথমেই জেনে নিই ওয়েব কিভাবে কাজ করে

আপনি যখন ব্রাউজারে www.medium.com/@cyantarek লিখেন তখন সেটা ব্রাউজার থেকে মিডিয়াম এর সার্ভারে একটা HTTP রিক্যুয়েস্ট পাঠায় এই বলে যে “তোমাদের সার্ভারে থাকা Cyan Tarek এর প্রোফাইলটা আমার দরকার” অথবা “আমার অমুক জিনিসটা দরকার”। এখানে জিনিস মানে হলো “রিসোর্স”

মিডিয়াম এর ওয়েব সার্ভার সেই রিক্যুয়েস্টটি পায় এবং প্রসেস করে রেসপন্স পাঠায় ইউজার এর ব্রাউজারে এই বলে যে “এই নেও তোমার চাওয়া জিনিস” অথবা “তোমার চাওয়া জিনিসটি পাওয়া যায় নি, দুঃখিত”। এগুলি সবই হয় ইন্টারনেট এর মাধ্যমে।

এই রিক্যুয়েস্ট/রেসপন্স আসলে বাংলা বা সাধারণ ইংরেজি ভাষায় পাঠানো হয় না। এটার একটা নিজস্ব রুল ও ফরমেট আছে। যেটাকে “প্রোটোকল” বলে।

উপরে বলেছি, এসব কিছুই হয় ইন্টারনেট বা নেটওয়ার্কের মাধ্যমে। নেটওয়ার্ক জিনিসটা সিম্পল হলে ভালোই হতো। কিন্ত এতটা সিম্পল না। ক্লায়েন্ট রিক্যুয়েস্ট পাঠালো আর সার্ভার রেসপন্স পাঠালো, এতটা সহজ মোটেও না। এর মাঝে আরো কিছু কাজ করা লাগে কতগুলি লেয়ারের মাধ্যমে।

পরে যে ৫ টা লেয়ার দেখতে পাচ্ছেন এটাকে বলে TCP/IP লেয়ার।

সম্পুর্ন ইন্টারনেটটা হচ্ছে লেয়ার্ড কেক এর মতো। অনেক গুলি লেয়ার মিলে তৈরি :)

ব্রাউজার হচ্ছে অ্যাপ্লিকেশন। ব্রাউজার থেকে সার্ভারে রিক্যুয়েস্ট পাঠানো হলে সেটা অ্যাপ্লিকেশন লেয়ারে কাজ করে। কিন্ত এখানেই শেষ না। এই রিক্যুয়েস্টটি অ্যাপ্লিকেশন লেয়ার থেকে নেমে নেমে প্রতিটা লেয়ারে কিছু কাজ করে এরপর ফিজিক্যাল লেয়ার (যেমনঃ ইথারনেট কেবল) এর মাধ্যমে অপর প্রান্তের সার্ভারে যায়।

এখানে দেখতেই পাচ্ছেন, ক্লায়েন্ট (বাম পাশে) থেকে রিক্যুয়েস্ট পাঠালে সেটা লেয়ার বাই লেয়ার নেমে নেমে কানেকশনের মাধ্যমে অপর প্রান্তের সার্ভারে গিয়ে আবার লেয়ার বাই লেয়ার উপরে ওঠে।

উপরে আমি বলেছিলাম প্রোটোকলের কথা। একধরনের লেয়ার আরেক ধরনের লেয়ারের সাথে কথা বলতে পারে না। শুধুমাত্র একই ধরনের লেয়ার নিজেদের মাঝে কথা বলতে পারে। যেমনঃ অ্যাপ্লিকেশন লেয়ার ও অ্যাপ্লিকেশন লেয়ারের মাঝে যোগাযোগ। কিন্ত এই যোগাযোগ এর জন্য প্রয়োজন হয় প্রোটোকল এর। তাই প্রতিটা লেয়ারের নিজস্ব কিছু প্রোটোকল আছেঃ

তাহলে আমরা দেখতেই পাচ্ছি, আমরা যে HTTP রিক্যুয়েস্ট পাঠাই সেটা মূলত অ্যাপ্লিকেশন লেয়ারের জন্য। তাই HTTP হচ্ছে অ্যাপ্লিকেশন লেয়ার প্রোটোকল। কারণ এই প্রোটোকলের মাধ্যমে দুটি অ্যাপ্লিকেশন কম্যুনিকেট করতে পারে নেটওয়ার্ক এর মাধ্যমে।

যেমনঃ আমাদের ক্ষেত্রে ব্রাউজার হচ্ছে একটা অ্যাপ্লিকেশন আর ওয়েব সার্ভার হচ্ছে আরেকটা অ্যাপ্লিকেশন।

কিন্ত HTTP প্রোটোকল দিয়ে অ্যাপ্লিকেশন লেয়ার থেকে সরাসরি তার দিয়ে যেহেতু ডাটা যেতে বা আসতে পারে না তাই আমরা এবার আসি এর এক ধাপ নিচের লেয়ারেঃ ট্রান্সপোর্ট লেয়ার

ব্রাউজার থেকে যখন HTTP রিক্যুয়েস্ট পাঠানো হয় তখন সেটা অ্যাপ্লিকেশন লেয়ারে কাজ শেষ করে ট্রান্সপোর্ট লেয়ারে আসে। ট্রান্সপোর্ট লেয়ার তখন ক্লায়েন্ট আর হোস্টের মাঝে একটা কানেকশন তৈরি করে যে কানেকশনের মাধ্যমে ডাটা ক্লায়েন্ট থেকে সার্ভারে যেতে পারে। এর জন্য ব্রাউজার প্রথমে একটা TCP সকেট খুলে দেয় ঐ সার্ভার এর এড্রেস আর তার পোর্টের সাথে। এরপর ডাটা আদান প্রদান শুরু হয়। ব্রাউজারের কাজ শুধু HTTP রিক্যুয়েস্ট পাঠানো, TCP প্রোটোকল সেই রিক্যুয়েস্ট ও ডাটাকে সার্ভারে পাঠানোর ব্যবস্থা করে সকেট কানেকশনের মাধ্যমে।

আমরা যেসকল ওয়েব সার্ভারগুলি ব্যবহার করি সেগুলি সবই টিসিপি কানেকশন নির্ভর সার্ভার। কিন্ত তারা যেহেতু ব্রাউজার থেকে রিক্যুয়েস্ট এক্সেপ্ট করবে আবার ব্রাউজারেই রেসপন্স পাঠাবে তাই এসব সার্ভার টপ লেভেলে এপ্লিকেশন লেয়ারও রেখে দেয়।

এবার আসুন আমরা একদম লোয়ার লেভেল থেকে শুরু করিঃ ট্রান্সপোর্ট লেয়ার।

প্রথমেই আমরা একটি TCP নির্ভর সকেট সার্ভার বানাবোঃ

import socket

HOST, PORT = '', 8888

listen_socket = socket.socket()
listen_socket.bind((HOST, PORT))
listen_socket.listen(1)

while True:
client_connection, client_address = listen_socket.accept()
request = client_connection.recv(1024)

print(request.decode())
print(client_connection)

response = "Hello World"
client_connection.sendall(response.encode())
client_connection.close()

আসুন একটু ব্যাখ্যা করিঃ

প্রথমেই আমরা হোস্ট ও পোর্ট ডিফাইন করে নিলাম, খালি হোস্ট মানে লোকালহোস্ট বা 0.0.0.0

এরপর আমরা এই কানেকশনের জন্য একটি সকেট বানালাম সার্ভারে, এড্রেসের সাথে বাইন্ড করলাম এবং রিক্যুয়েস্ট পাবার জন্য অপেক্ষা করতে থাকলাম একটা লুপের মাধ্যমে।

অন্য কোন সকেট থেকে টিসিপি কানেকশনের মাধ্যমে যখন কোনো রিক্যুয়েস্ট আসবে তখন সেটাকে আমরা ভেঙ্গে ক্লায়েন্ট এর সকেট কানেকশন আর এড্রেস বের করবো। আগেই বলেছিলাম, অপর প্রান্ত থেকে টিসিপি কানেকশনে কানেক্ট করতে হলে ঐ প্রান্তেও একটা সকেট বানাতে হয় যা কানেকশন হিসেবে কাজ করে। ব্রাউজার থেকে রিক্যুয়েস্ট পাঠালে ব্রাউজার অটোম্যাটিকভাবে একটা সকেট কানেকশন বানিয়ে কানেক্ট করে সার্ভারের সাথে যেটা উপরেই বলেছি।

এরপর আমরা আমাদের রেসপন্সটি ক্লায়েন্ট এর সকেট এ পাঠালাম স্ট্রিং থেকে বাইটে ডিকোড করে। কারণ ট্রান্সপোর্ট লেয়ারে ডাটা ট্রান্সপোর্ট হয় বাইট স্ট্রিম হিসেবে।

এবার একটা ব্রাউজারে গিয়ে 127.0.0.1:8888 এড্রেসে যান। নিচের মত এরোর স্ক্রিন পাবেনঃ

এর মানে কি? খেয়াল করে দেখুন এরোর মেসেজটাঃ ERR_INVALID_HTTP_RESPONSE

হুম, সার্ভার ঠিকই রেসপন্স পাঠাচ্ছে কিন্ত ব্রাউজারের মন মতো হচ্ছে না তাই ইনভ্যালিড বলছে। কি জন্য মন মতো হচ্ছে না?

সার্ভার ঠিকই রেসপন্স পাঠাচ্ছে কিন্ত সেটা এপ্লিকেশন লেয়ার পর্যন্ত সঠিক ফরমেটে আসতেছে না। তাই রেসপন্সটা ইনভ্যালিড দেখাচ্ছে। এর অন্যতম কারণ হলো, আমাদের টিসিপি সার্ভারটি এখন শুধু ট্রান্সপোর্ট লেয়ার পর্যন্ত কাজ করতেছে।

এটা নিশ্চিত হবার জন্য টার্নিমানল থেকে telnet দিয়ে একটা রিক্যুয়েস্ট পাঠিয়ে দেখুনঃ

telnet 127.0.0.1 8888

বাহ! এখানে কিন্ত ঠিকই রেসপন্স দেখাচ্ছে। কারণ telnet দিয়ে টিসিপি রিক্যুয়েস্ট পাঠানো হয়। আর ব্রাউজার দিয়ে পাঠানো হয় HTTP রিক্যুয়েস্ট। কিভাবে নিশ্চিত হবেন?

আপনার টার্মিনালটি দেখুন যেখানে সার্ভারটি রান করেছেনঃ

সবুজ অংশটি হচ্ছে আমাদের ব্রাউজার থেকে পাঠানো HTTP রিক্যুয়েস্ট, একদম প্রোপারলি ফরমেট করা

লাল অংশটি হচ্ছে ব্রাউজার যে সকেট কানেকশন তৈরি করেছে সেটি।

আর গোলাপি অংশটি হচ্ছে telnet থেকে আমরা যে রিক্যুয়েস্ট পাঠিয়েছি সেটি, দেখুন এটি শুধুমাত্র একটা সকেট কানেকশন থেকে রিক্যুয়েস্ট ছিলো।

এটি হচ্ছে যেকোন ওয়েব সার্ভারের একদম মৌলিক অংশ। প্রতিটি ওয়েব সার্ভারই টিসিপি সকেট সার্ভারকে বেজ করে কাজ করে।

আমাদের সার্ভারটি HTTP রেসপন্স করতে পারছে না। কিন্ত খুব সহজেই আমরা তা করতে পারি HTTP প্রোটোকল এর ফরমেট অনুসারে। কোডে নিচের মত করে একটু চেঞ্জ করুনঃ

import socket

HOST, PORT = '', 8888

listen_socket = socket.socket()
listen_socket.bind((HOST, PORT))
listen_socket.listen(1)

while True:
client_connection, client_address = listen_socket.accept()
request = client_connection.recv(1024)

print(request.decode())
print(client_connection)

response = """\
HTTP/1.1 200 OK

Hello, World!
"""

client_connection.sendall(response.encode())
client_connection.close()

এবার সার্ভার রান করে ব্রাউজার থেকে রিক্যুয়েস্ট পাঠিয়ে দেখুনঃ

বাহ! এবার কিন্ত ঠিকই রেসপন্স পাঠিয়েছে। কিভাবে?

আমরা আমাদের রেসপন্সকে HTTP এর নিয়মানুসারে ফরমেট করেছি শুরুতে স্ট্যাটাস লাইন যোগ করে। তাই ব্রাউজার যখন এটি পাবে তখন ব্রাউজার স্ট্যাটাস লাইন দেখেই বুঝবে এটি একটি HTTP রেসপন্স। তাই সে ব্রাউজারে দেখাবে।

এখন যা দেখলেন এটার উপর ভিত্তি করেই HTTP সার্ভারগুলি কাজ করে থাকে। Flask, Django ইত্যাদির ডেভলপমেন্ট সার্ভারগুলি টিসিপি সার্ভারের উপরের লেয়ারে HTTP এর ফীচার যোগ করে দেয়, যেমনঃ রিক্যুয়েস্ট হ্যান্ডলিং, কুকি, সেশন, রেসপন্স হ্যান্ডলিং ইত্যাদি। আপনি এগুলি ইমপ্লিমেন্ট করে খুব সহজেই আপনার নিজের একটি HTTP সার্ভার বানিয়ে ফেলতে পারেন।

আজকে এই পর্যন্তই। আজকের পার্টে আমি দেখালামঃ

১. বেসিক নেটওয়ার্কিং কনসেপ্ট

২. বিভিন্ন নেটওয়ার্ক লেয়ার

৩. প্রোটোকল

৪. পিওর TCP সকেট সার্ভার বানানো

৫. সকেট সার্ভারের উপরের লেয়ারে HTTP ফীচার যুক্ত করে HTTP সার্ভার বানানো

এই পর্বে শুধু একদম অল্প একটুখানি HTTP সার্ভার এর কাজ দেখালাম টিসিপি সার্ভারের উপর ভিত্তি করে। আজকে এই পর্যন্তই। আগামী পর্বে দেখাবো কীভাবে করে একটি হাই লেভেল HTTP সার্ভার বানাতে হয়। এবং ৩য় পর্বে দেখাবো WSGI কি এবং কীভাবে করে একটি WSGI সার্ভার বানাতে হয়।

আল্লাহ হাফেজ।

--

--

Cyan Tarek
প্রোগ্রামিং পাতা

Software Engineer, Backend Ninja, DevOps Player, Microservice learner, Gopher/Golang Lover