প্রোগ্রামিং শেখার এক ডজন টিপস্

 প্রথম প্রোগ্রামিং ল্যাঙ্গুয়েজ হিসেবে সি (C) বেশ জনপ্রিয় ও বহুল ব্যবহৃত। বিভিন্ন প্রোগ্রামিং প্রতিযোগিতায় (স্কুল-কলেজ-বিশ্ববিদ্যালয় পর্যায়ে) সি ব্যবহার করা হয়। কলেজে আইসিটি কোর্সের সি ব্যবহার করা হয় এবং বেশিরভাগ বিশ্ববিদ্যালয়েও প্রথম প্রোগ্রামিং কোর্সে সি ব্যবহার করা হয়। সি ভালোভাবে শিখতে গিয়ে অনেকেই নানান সমস্যায় পরে। তাই আমার অভিজ্ঞতা থেকে কিছু পরামর্শ দিলাম।

০) কোর্স পাশ করা কিংবা একটা বই পড়ে শেষ করা উদ্দেশ্য নয়, সেটা বুঝতে হবে। কম্পিউটার সায়েন্সে পড়ে প্রোগ্রামিং ঠিকমতো না শিখলে সারা জীবন ভুগতে হবে। প্রোগ্রামিং কোর্সে এ প্লাস পাওয়া আর প্রোগ্রামিং শেখা এক জিনিস নয় – এই জিনিস মাথায় গেঁথে নিতে হবে।

১) প্রোগ্রামিংয়ের পেছনে নিয়মিত সময় দিতে হবে। একটানা কয়েকঘণ্টা (২ থেকে ৫ ঘণ্টা)। সপ্তাহে কমপক্ষে ৫ দিন। এভাবে মাসের পর মাস। প্রোগ্রামিং করার সময় অন্য কিছু, যেমন টিভি দেখা, খেলা দেখা, ফেসবুক – এসব করা যাবে না।

২) প্রথমে একটা বিষয় পড়ে বোঝার চেষ্টা করতে হবে। প্রথম পড়াতেই পুরোটা বুঝতে পারা যাবে না (বেশিরভাগ ক্ষেত্রেই) এবং তাতে কোনো অসুবিধা নেই। যেকোনো একটা বই দিয়ে শুরু করতে হবে এবং বইয়ের প্রত্যেকটা উদাহরণ নিজে প্রোগ্রাম লিখে চালিয়ে দেখতে হবে।

৩) প্রোগ্রামিংয়ে অনেক সময়ই কেন হয় প্রশ্নের উত্তর পাওয়া যাবে না (আসলে পাওয়া যাবে, তবে অনেক পরে, কখনও কয়েক মাস পরে, কখনও আরো বেশি সময় পরে)। যেমন: scanf ফাংশনে ভেরিয়েবলের আগে & চিহ্ন কেন ব্যবহার করা হয়, সেটা নিয়ে শুরুতে বেশি মাথা ঘামানো দরকার নাই। কিভাবে ব্যবহার করতে হয়, সেটা জানলেই হবে। তবে প্রোগ্রামিং শিখতে থাকলে একসময় এর উত্তর পেয়ে যাবে। শুরুতে কী করলে কী হয়, সেটাই গুরুত্বপূর্ণ। এজন্য বেশি বেশি প্রোগ্রাম লিখতে হবে, এবং প্রোগ্রামিং করার সময় পূর্ণ মনোযোগ দিতে হবে।

৪) কোনো কিছু মনে রাখার চেষ্টা করা, কিংবা মুখস্থ করার চেষ্টা করার দরকার নাই। প্রোগ্রামিং এত বেশি প্র্যাকটিস করতে হবে যে মুখস্থ না করে বিষয়টা নিজের আয়ত্বে এসে যাবে। ক্রিকেট খেলায় যেমন কোন ধরনের বলে কোন শট খেলতে হয়, সেটা কেউ মুখস্থ করে না, বরং প্র্যাকটিস করতে করতে আয়ত্বে চলে আসে – এরকম আর কী।

৫) লজিক জিনিসটা খুবই গুরুত্বপূর্ণ ও সহজ। কিন্তু আমাদের সবাই প্রোগ্রামিং শিখতে আসলে এখানে ধাক্কা খাই। কারণ আসলে তো আমরা সারাজীবন মুখস্থ করে এসেছি, চিন্তাভাবনা করে কিছু করি নাই ও শিখি নাই, আর সমসময়ই শিক্ষক বলে দিয়েছেন যে কী করতে হবে। তাই শুরুতে ধাক্কা খেলে হতাশ হওয়া চলবে না, বরং ধৈর্য্য রাখতে হবে, চেষ্টা চালিয়ে যেতে হবে।

৬) যেই বই দিয়েই সি শেখা শুরু কর না কেন, সেটা দুইবার পড়তে হবে (এবং সেই বইতে যা বলা হয়েছে, তা করতে হবে)। তারপরে কমপক্ষে আরো একটা বই পড়তে হবে।

৭) ক্লাসের পরীক্ষায় (মানে প্রোগ্রামিং কোর্সের পরীক্ষায়) কম নাম্বার পেলে মন খারাপ করা চলবে না। কে কতটুকু প্রোগ্রামিং পারে সেটা আসলে পরীক্ষায় যাচাই করা খুব কঠিন।

৮) i++, ++i এসব জিনিস নিয়ে শুরুর দিকে মাথা ঘামানো মানে সময় নষ্ট করা, যদিও এটা পরীক্ষায় অনেক শিক্ষকেরই প্রিয় প্রশ্ন। ভেরিয়েবল, কন্ডিশনাল লজিক, লুপ, অ্যারে, ফাংশন – এসব জিনিস ভালোভাবে শিখতে ও এগুলো ব্যবহার করতে পারতে হবে।

৯) সি মোটামুটি শেখা হয়ে গেলে সি দিয়ে যেকোনো ওয়েবসাইট থেকে ৫০-১০০ টা সমস্যা সমাধান করতে হবে, তাহলে হাত ও মাথা পাকবে।

১০) আমি সি পুরোটা শিখতে চাই – এই টাইপ চিন্তাভাবনা থেকে বের হয়ে আসতে হবে। আমার মতে, এটা বোকার মত চিন্তা। পুরোটা সি শেখা বলতে আসলে তুমি কী বোঝাও, সেটা নিজেই জান না। অনেকে সি দিয়ে গ্রাফিক্যাল ইউজার ইন্টারফেস (গুই – GUI) সমৃদ্ধ সফটওয়্যার বানাতে চায়, যেটা আসলে না করলেই ভালো। কারণ এতে পরিশ্রম বেশি হয়, শেখা কম হয়। প্রোগ্রামিং শিখতে থাকলে একসময় গুই বানানোর অনেক টুলসের সাথে পরিচিত হবে, তাই অস্থির হওয়ার কিছু নেই।

১১) প্রোগ্রামিং কনটেস্টে অংশ নিতে হবে। প্রোগ্রামিং প্রতিযোগিতায় ভালো করতে পারলে তো ভালো, কিন্তু ভালো করতে না পারলেও ক্ষতি নেই। ভালো করার জন্য যেই চেষ্টা – সেটা করতে গিয়েই অনেক কিছু শিখতে পারবে যেটা তোমাকে ভবিষ্যতে একজন দক্ষ সফটওয়্যার ইঞ্জিনিয়ার হতে সহায়তা করবে।

প্রোগ্রামিং নিয়ে আনন্দে সময় কাটুক সবার!

নোট: প্রোগ্রামিং শুরু করার আরো কিছু গাইডলাইন এখানে আছে : http://programabad.com/questions/1447/-

প্রোগ্রামিং ল্যাঙ্গুয়েজ

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

আমি যখন কলেজে পড়তাম, তখন কিউ-বেসিক (Q Basic) নামে একটা প্রোগ্রামিং ল্যাঙ্গুয়েজ শেখার ব্যর্থ চেষ্টা করেছিলাম। কিউ-বেসিক ল্যাঙ্গুয়েজটা আমাদের সিলেবাসে ছিল আর কী। এইচএসসি পরীক্ষার পরে কম্পিউটার কিনি, তখন এইচটিএমএল (HTML) শিখলাম কিছুটা, যদিও এটা ঠিক প্রোগ্রামিং ল্যাঙ্গুয়েজ না। তারপরে ২০০১ সালের শুরু থেকে সি (C) শেখা শুরু করি। আমাদের ভার্সিটির ক্লাস শুরু হয় ২০০১ সালের মে মাসে, আর ততদিনে সি এর বেসিক কিছুটা শেখা হয়েছে। আমি আমার বাকী ভার্সিটি-জীবন এর সুবিধা ভোগ করি। আমার যদি ভার্সিটির সি কোর্সের ক্লাশ থেকে সি শেখা লাগতো, তাহলে একটু অসুবিধাই হতো হয়ত, যেটা আমার অনেক ক্লাসমেটকে দেখে বুঝতে পেরেছি। সি শেখার সময় প্রথম বছরে ৩-৪টা বই কিনেছিলাম, যদিও সবগুলো বই শুরু থেকে শেষ পর্যন্ত পড়ি নাই। এক বছর সি শেখার পরে আমি হার্বার্ট শিল্ডের টিচ ইয়োরসেল্ফ সি প্লাস প্লাস বইটা পড়ি। কিন্তু এরপরে আর আমার সি প্লাস প্লাস (C++) চর্চা করা হয় নাই।

ভার্সিটির ফার্স্ট ইয়ারে একটা ছোট প্রজেক্ট করতে হয়েছিল, যেখানে এইচটিএমএল-এর পাশাপাশি একটু জাভাস্ক্রিপ্টও (Javascript) ব্যবহার করতে হয়েছিল। সেই ২০০২ সালের শুরুর দিকের ঘটনা। অল্প একটু জাভাস্ক্রিপ্ট শিখেছিলাম। আমার ইচ্ছা ছিল আমার এক বন্ধুকে দিয়ে প্রজেক্টটা করিয়ে ফেলবো। তো ব্যাটা রাত ২টার দিকে ঘুমিয়ে গেল, আমি চারটা পর্যন্ত অপেক্ষা করলাম। কারণ ওইদিনই প্রজেক্ট জমা দিতে হবে। চারটার পরে আমি আর কোনো উপায় না দেখে নিজেই কাজ করতে বসে গেলাম এবং শেষ পর্যন্ত কাজটা করেও ফেললাম। এরপরেও মাঝে মাঝে জাভাস্ক্রিপ্ট ব্যবহার করতে হয়েছিল, কিন্তু তেমন ভালোভাবে শেখা হয় নাই। আর গত চার-পাঁচ বছরে জাভাস্ক্রিপ্টে কিছুই করি নাই।

ভার্সিটির সেকেন্ড ইয়ারে অবজেক্ট ওরিয়েন্টেড কোর্স ছিল। সেই কোর্সে জাভা (Java) শিখি। আর তারপরের সেমিস্টারে একটা প্রজেক্টও করি জাভা ব্যবহার করে। তারপরে ২০০৬-২০০৭ সালে কিছু টুকটাক কাজ করতে জাভা ব্যবহার করেছিলাম।

সম্ভবত ফোর্থ ইয়ারে আমাদের মাইক্রোপ্রসেস কোর্স ছিল, সেই কোর্সের ল্যাবের জন্য এসেম্বলি ল্যাঙ্গুয়েজ (Assembly Language) শিখতে হয়েছিল।

আমার প্রথম প্রফেশনাল চাকরি ছিল টাইগার আইটি-তে, ২০০৭ সালের মাঝামাঝি। সেখানে আমি যেই প্রজেক্টে জয়েন করলাম, সেই প্রজেক্টে পার্ল (Perl) প্রোগ্রামিং ল্যাঙ্গুয়েজ ব্যবহার করতে হয়। কিন্তু আমি আগে কখনও এই ল্যাঙ্গুয়েজের নাম শুনি নাই। তাই বলে আমার কিন্তু চাকরি পেতে সমস্যা হয় নাই। কারণ ভালো কোম্পানীগুলো বেশি গুরুত্ব দেয় প্রবলেম সলভিং স্কিলের ওপর, প্রোগ্রামিং ল্যাঙ্গুয়েজ শেখার ওপর নয়।

আমার দ্বিতীয় সফটওয়্যার ইঞ্জিনিয়ারিং চাকরি ছিল ট্রিপার্ট ল্যাবসে (যেটা পরে প্লেডম কিনে নেয়, আবার প্লেডমকে ডিজনী কিনে নেয়)। সেখানে ব্যাকএন্ডের কাজ হতো পিএইচপি (PHP) প্রোগ্রামিং ল্যাঙ্গুয়েজে। আর মাঝেমধ্যে অল্পস্বল্প জাভাস্ক্রিপ্ট। তো সেই চাকরির ইন্টারভিউতে আমাকে জিজ্ঞাসা করল যে পিএইচপি পারি কী না। আমি সোজা বলে দিলাম যে পারি না, কারণ এর আগে ২-৩ দিন পিএইচপি নিয়ে গুতাগুতি করলেও সেটা বলার মতো কিছু না। কিন্তু সেজন্য আমার চাকরি পাওয়া আটকায় নাই। সেখানে এক বছর কাজ করার পরে একটা নতুন প্রজেক্টে কাজ করতে হয়, আর সেই প্রজেক্ট হচ্ছে অ্যাকশন স্ক্রিপ্ট (Actionscript)-এ। তো সেটাও দুই-তিন দিন শিখে কাজ শুরু করে দেই।

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

২০১৫ সালের মাঝামাঝি আমি সিঙ্গাপুরের গ্র্যাব নামক কোম্পানীতে ইন্টারভিউ দেই। বেশ কয়েকটা ইন্টারভিউ হয় এবং সেখানে আমি সি ব্যবহার করি, প্রবলেম সলভ করার জন্য। ইন্টারভিউ শেষে অফার পেয়ে জয়েন করি। জয়েন করার পরে জানতে পারলাম যে এখানে গো (Go বা Golang) ব্যবহার করা হয়, তাই প্রথম কিছুদিন গো শিখি। লক্ষ্য করার বিষয় হচ্ছে এত বড় কোম্পানী এত বেতন দিয়ে অন্য দেশ থেকে ইঞ্জিনিয়ার হায়ার করছে, ওরা কিন্তু এই বিষয় নিয়ে মাথা ঘামায় নাই যে আমি গো পারি কী না। সুতরাং বোঝাই যাচ্ছে যে কয়টা প্রোগ্রামিং ল্যাঙ্গুয়েজ পারি, এটা আসলে বিবেচ্য বিষয় নয়। কম্পিউটার সায়েন্সের বেসিক জ্ঞান (ডাটা স্ট্রাকচার, অ্যালগরিদম, অপারেটিং সিস্টেম, ডাটাবেজ, অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং, নেটওয়ার্কিং ইত্যাদি) এবং প্রবলেম সলভিং স্কিল-ই একজন ভালো সফটওয়্যার ইঞ্জিনিয়ার হওয়ার পূর্বশর্ত। আর কাজ করতে করতে সফটওয়্যার ইঞ্জিনিয়ারিংটাও শেখা হয়ে যায়, তবে তার জন্য ভালো কোম্পানীতে কাজ করাটা জরুরী।

বাইনারি সার্চ-এর কোড

আজকে সকালে গো (Go) প্রোগ্রামিং ল্যাঙ্গুয়েজে একটু কোডিং প্র্যাকটিস করার জন্য বাইনারি সার্চ ফাংশন লিখলাম, তারপরে ইউনিট টেস্ট লিখলাম (টেবল ড্রিভেন টেস্ট)। তো এমন সময় আমার মনে হলো লাইব্রেরি ফাংশনগুলো কিভাবে ইমপ্লিমেন্ট করা, সেটা দেখা দরকার। তো গো এর লাইব্রেরিতে যে বাইনারি সার্চ আছে, তার কোড (এবং ডকুমেন্টেশন) দেখে তো আমি মুগ্ধ!
এটা হচ্ছে গো-এর কোড:

func Search(n int, f func(int) bool) int {
    // Define f(-1) == false and f(n) == true.
    // Invariant: f(i-1) == false, f(j) == true.
    i, j := 0, n
    for i < j {
	h := i + (j-i)/2 // avoid overflow when computing h 
        if !f(h) { 
            i = h + 1 // preserves f(i-1) == false 
        } else { 
            j = h // preserves f(j) == true 
        } 
    } 
    return i
}

ওপরের কোডটুকু দেখলে আসলে পুরোপুরি বোঝা যাবে না কেন আমি এত মুগ্ধ। এই লিঙ্কে গেলে বিষয়টা আরো পরিষ্কার হবে: https://golang.org/src/sort/search.go। কোডের চেয়ে ডকুমেন্টেশন অনেক বেশি। আর এই বেশি ডকুমেন্টেশনসহ কোড হচ্ছে ১১৩ লাইন। কিন্তু বিষয় এখানেই শেষ নয়। আমার তারপরে চিন্তা আসল, আচ্ছা, এত সুন্দর কোড আর ডকুমেন্টেশন – এই কোডের টেস্ট কোড (ইউনিট টেস্ট) ওরা কিভাবে লিখল? তার জন্য চলে গেলাম এই লিঙ্কেঃ https://golang.org/src/sort/search_test.go। ১১৩ লাইনের search.go এর জন্য search_test.go তে আছে ১৬২ লাইন। এই দুইটা ফাইলের কোড এবং ডকুমেন্টেশন ঠিকমতো পড়লে শেখার আছে অনেক কিছুই।

তারপর চিন্তা করলাম, গো এর কোড এত সুন্দর, সি এর লাইব্রেরিতে এটা কিভাবে করা হয়েছে? গুগল সার্চ করে খুঁজে বের করলাম কোড।

void *
bsearch (register const void *key, const void *base0,
    size_t nmemb, register size_t size,
    register int (*compar)(const void *, const void *))
{
    register const char *base = (const char *) base0;
    register int lim, cmp;
    register const void *p;

    for (lim = nmemb; lim != 0; lim >>= 1) {
        p = base + (lim >> 1) * size;
        cmp = (*compar)(key, p);
        if (cmp == 0)
            return (void *)p;
        if (cmp > 0) {	/* key > p: move right */
            base = (const char *)p + size;
            lim--;
        } /* else move left */
    }
    return (NULL);
}

এই কোডটাও বেশ সুন্দর, গুছানো। ডকুমেন্টেশনও ভালো। আর পয়েন্টার দেখে ভয় পাওয়ার কারণ নাই (পয়েন্টার নিয়ে আমি কম্পিউটার প্রোগ্রামিং ২য় খণ্ড বইতে বিস্তারিত আলোচনা করেছি অনেক উদাহরণসহ)। তবে এখানে গিয়ে পুরো ফাইলটা না দেখলে এর মর্ম বোঝা যাবে না : https://github.com/gcc-mirror/gcc/blob/master/libiberty/bsearch.c

গো আর সি-এর কোড যেহেতু দেখলাম, পাইথনের কোডটাও দেখা যাক। তাই সেটাও সার্চ করে বের করে ফেললাম :

def bisect_left(a, x, lo=0, hi=None):
    """Return the index where to insert item x in list a, assuming a is sorted.

    The return value i is such that all e in a[:i] have e < x, and all e in a[i:] have e >= x.  So if x already appears in the list, a.insert(x) will
    insert just before the leftmost x already there.

    Optional args lo (default 0) and hi (default len(a)) bound the
    slice of a to be searched.
    """

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if a[mid] < x: lo = mid+1
        else: hi = mid
    return lo

গো আর সি এর তুলনায় বেশ সহজ! তবে ডকুমেন্টেশন সংক্ষিপ্ত হলেও ভালো। এখানে গিয়ে পুরো কোড দেখা যাবে : https://github.com/python-git/python/blob/master/Lib/bisect.py

তারপরে ভাবলাম, আমার ব্লগের পাঠকদেরকে বিষয়টা জানানো যাক। আশা করি, বিশ্বমানের প্রোগ্রামারদের কোডের সঙ্গে পাঠকদের কিছুটা হলেও পরিচয় করিয়ে দিতে পেরেছি। কোডিং সঠিক হোক, কোড সুন্দর হোক।

সি দিয়ে ওয়েব সার্ভার

এসো নিজে করি : ওয়েব সার্ভার

লেখক : ওমর শেহাব

এটি একটি শিশুতোষ লেখা। যারা মাত্র সি প্রোগ্রামিং শেখা শুরু করেছে তাদের জন্য এটি লিখছি যাতে তারা সি ল্যাংগুয়েজের সামর্থ্য সম্পর্কে কিছুটা ধারণা পায়।

আজকে থেকে এগার বছর আগের কথা। আমি তখন শাবিপ্রবি কম্পিউটার বিজ্ঞান ও প্রকৌশল বিভাগে তৃতীয় বর্ষে পড়ছি। আমাদের একটা কোর্স ছিল সিএসই ৩৫০। এই কোর্সে একটি সফটওয়্যার বানাতে হত। তখন আমার শিক্ষক শাহরিয়ার ভাইয়ের কাছ থেকে সি ল্যাংগুয়েজ দিয়ে একটি ওয়েব সার্ভার বানানোর আইডিয়া পেলাম।

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

সেই কোডটি এই লিংকে (http://bit.ly/1GEzoX7) পাওয়া যাবে। আমি ভাবলাম যারা মাত্র সি শেখা শুরু করেছে তাদের কেউ কেউ এই ব্যাপারে আগ্রহী হতে পার। আমি একটু একটু করে এই কোডের ব্যাপারগুলো ধরিয়ে দিব। একটি জিনিস মনে রাখতে হবে আমি তখনও ছাত্র ছিলাম এ কারণে কোডের মান খুব বেশি আহামরি না।

তাহলে শুরু হোক! আমি একটি একটি করে ব্লক দিব আর তার নিচে ওই ব্লক নিয়ে কথা বলব।

/*
 * Course Code 350
 *
 * A Multithreaded Tiny HTTP Server
 * Demonstrating Thread Pool Management
 * Following the Thread Pool Management
 * Outline from Unix Network Programming Vol 1
 * by W. Richard Stevens.
 *
 * Project accomplished by: Abu Mohammad Omar Shehab Uddin Ayub
 * Reg No. 2000 330 096
 * Section B
 * 3rd Year 2nd Semester
 * Dept of CSE, SUST
 *
 * Idea and guided by: Mahmud Shahriar Hussain
 * Lecturer
 * Dept of CSE, SUST
 *
 * Course Instructor: Rukhsana Tarannum Tazin
 * Lecturer
 * Dept of CSE, SUST
 */

যারা ইতিমধ্যে প্রোগ্রামিং শুরু করেছ তারা নিশ্চয়ই জান যেকোন কোডের আগে কমেন্টে এই কোডের উদ্দেশ্য ও প্রোগ্রামারের নাম দেয়া স্বাভাবিক ভদ্রতা।

#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

সি ল্যাংগুয়েজের নিজস্ব যে ফিচার বা সাপোর্ট আছে সেগুলো বিভিন্ন হেডার ফাইলে কোড করা থাকে। যেমন আমি পোসিক্স থ্রেড নিয়ে কাজ করতে চাই কাজেই পিথ্রেড.এইচ ফাইলটি ব্যবহার করছি।

#define MAX_CLIENT 5
#define MAX_THREAD 3
#define MAX_REQ_HEADER 10
#define MAX 10
#define MAX_LENGTH 6
#define MIME_LENGTH 20

এখানে আমি আগে থেকে কিছু জিনিস ঠিক করে নিচ্ছি। যেমন: আমি চাই এক সাথে পাঁচটির বেশি ব্রাউজার (ফায়ারফক্স, ক্রোম এগুলোকে ব্রাউজার বলে) আমার সার্ভারের পেজ ব্রাউজ করতে পারবে না কাজেই আমি MAX_CLIENT 5 দিয়েছি।

এর পর আমি আমার পছন্দ মত কিছু ডেটা স্ট্রাকচার ঠিক করে নিব।

typedef struct {
   pthread_t threadID;
   long threadCount;
}Thread;

যেমন থ্রেড সম্পর্কিত তথ্য রাখার জন্য আমি থ্রেড নামে একটি স্ট্রাকচার ব্যবহার করছি।

typedef struct {
   char hostName[100];
   char filePath[100];
}Request;

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

typedef struct{
   int code;
   char date[30];
   char server[30];
   char last_modified[30];
   long content_length;
   char content_type[10];
   char file_path[80];
}Reply;

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

Thread thrdPool[MAX_THREAD];
Request request[MAX_THREAD];
Reply reply[MAX_THREAD];
int clntConnection[MAX_CLIENT], clntGet, clntPut;

এখানে আমি আমার সার্ভারের লোড নেবার সামর্থ‌্য ঠিক করে দিচ্ছি। লোড মানে সে একসাথে কয়জন ওয়েব ব্যবহারকারীর রিকোয়েস্ট সামলাতে পারবে।

thread_mutex_t clntMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t srvrMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t srvrMutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t emitMutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clntCondition = PTHREAD_COND_INITIALIZER;

মিউটেক্স জিনিসটা হল মালটিথ্রেডেড প্রোগ্রামিংয়ে অনেকটা ছিটকিনির মত কাজ করে। একটি ওয়েবসার্ভারের কাছে যখন কোন পেজ দেখার রিকোয়েস্ট আসে তখন সে সেটি নিয়ে সারাক্ষণ ব্যস্ত থাকে না। সে কি করে একটি থ্রেড তৈরি করে সেই থ্রেডের কাছে সেই রিকোয়েস্টের দায়িত্ব দিয়ে নিজে নতুন রিকোয়েস্টের জন্য অপেক্ষা করতে থাকে। নতুন রিকোয়েস্ট আসলে সেটিকে নতুন আরেকটি থ্রেডের কাছে পাঠিয়ে দেয় আর আবারো সে আরেকটি রিকোয়েস্টের জন্য অপেক্ষা করতে থাকে। থ্রেডগুলো অপারেটিং সিস্টেমের সাহায্য নিয়ে নিজের মত করে রিকোয়েস্ট পরে পরে যে ওয়েবপেজটি দেখতে চাওয়া হয়েছে সেটি পাঠিয়ে দেয়। একটি থ্রেড কখনও জানতে পারে না অন্য থ্রেডের ভিতরে কি চলছে। তাদের সবার নিজস্ব জগৎ থাকে। তবে সবগুলো থ্রেডকে লাইনে রাখার জন্য কিছু কোড আছে, কিছু ভ্যারিয়েবল আছে। সেই ভ্যারিয়েবলগুলো সব থ্রেড দেখতে পারে। এখন একই ভ্যারিয়েবল নিয়ে যদি একাধিক থ্রেড একই সময়ে কাজ করতে চায় তাহলে তো ভজঘট লেগে যাবে। এখানেই মিউটেক্স কাজে আসে। কোন থ্রেড যখন কোন শেয়ারড ভ্যারিয়েবল নিয়ে কাজ করে মিউটেক্স বা ছিটকিনি তুলে দেয়। অন্য থ্রেড যখন কাজ করতে গিয়ে দেখে ছিটকিনি তোলা তখন তারা চুপচাপ অপেক্ষা করে। শেয়ারড ভ্যারিয়েবল নিয়ে কাজ শেষ হলে সেই থ্রেড ছিটকিনি বা মিউটেক্স নামিয়ে দেয়। তখন অপেক্ষা করতে থাকা অন্য থ্রেড কাজ শুরু করে আর আবারও ছিটকিনি তুলে দেয়।

static int numThreads;
void *request_handler(void *arg);
int find_method (char *req);
Request parse_request(char *rbuff);
Reply prepare_reply(int code, Request rq);
char *getMimeType(char *str);
void emit_reply(int conn, Reply rply);

একটু গুছিয়ে কাজ করার জন্য পুরো কোডটিকে আমি কিছু ফাংশনে ভাগ করেছি। এখানে সেগুলো ডিক্লেয়ার করলাম।

int main(void)
{

মেইন ফাংশন শুরু হল অর্থাৎ প্রোগ্রামের মূল অংশটি শুরু হচ্ছে।

int i, serverSockfd, clientSockfd;
int serverLen, clientLen;
struct sockaddr_in serverAddress, clientAddress, tempAddress;

কাজের সুবিধার জন্য কিছু ভ্যারিয়েবল দরকার।

//defining number of threads
 numThreads = MAX_THREAD;
// initializing queue parameters
 clntGet = clntPut = 0;

একবারে একসাথে কয়টি রিকোয়েস্ট নিয়ে কাজ করা যাবে অর্থাৎ সার্ভারের সামর্থ ঠিক করে দিচ্ছি।

//creating all the threads
for(i = 0; i < numThreads; i++)
{
    pthread_create(&thrdPool[i].threadID, NULL, &request_handler, (void *) i);
} // end of for loop

যখনই কোন রিকোয়েস্ট আসবে একটি করে থ্রেড তার দায়িত্ব নিবে। রিকোয়েস্ট আসার পরপর নতুন থ্রেড তৈরি করতে গেলে প্রোগ্রাম স্লো হয়ে যায় তাই আগেই কিছু তৈরি করে রাখি।

//creating the socket descriptor
serverSockfd = socket(AF_INET, SOCK_STREAM,0);
if(serverSockfd < 0)
{
   error("\nError opening socket");
   exit(1);
}
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = INADDR_ANY;
serverAddress.sin_port = htons(80);
serverLen = sizeof(serverAddress);
if(bind(serverSockfd, (struct sockaddr *)&serverAddress, serverLen) < 0)
{
   printf("\nError in binding.");
   exit(1);
}

অন্য যেকোন ওয়েবসার্ভারের মত আমার সার্ভারও কম্পিউটারের ৮০ নম্বর পোর্টে কান পেতে রাখবে। এখন আমার জানা জরুরী যে ইতিমধ্যে এই পোর্ট অন্য কোন সফটওয়্যার দখল করে ফেলেছে কিনা। সাবধানের মার নাই!

listen(serverSockfd, 10);

আমি কাআআআন পেতেএএএ রইইই!

for( ; ; )
{
   fflush(stdout);
   //printf("\nWaiting for clients...");
   clientLen = sizeof(tempAddress);
   //printf("\n\nBlocked on accept()");
   clientSockfd = accept(serverSockfd, (struct sockaddr *)&clientAddress, &clientLen);
   //printf("\nAllocated client descriptor# %d", clientSockfd);
   pthread_mutex_lock(&srvrMutex);
   clntConnection[clntPut] = clientSockfd;
   if(++clntPut == MAX_CLIENT)
   clntPut = 0;

   if(clntPut == clntGet)
   {
      printf("\nCan't handle any more request...\nTerminating...");
      exit(1);
   }

   pthread_cond_signal(&clntCondition);
   pthread_mutex_unlock(&srvrMutex);
   fflush(stdout);

} // end of infinite for loop

ইনফিনিটি লুপ খুব একটা ভাল জিনিস না। এটি আরো ভাল ভাবে করতে পারা উচিৎ। এখানে আমি যখনই কোন রিকোয়েস্ট পাচ্ছি ছিটকিনি তুলে দিয়ে একটি থ্রেড ধরে এনে তাকে রিকোয়েস্টের দায়িত্ব দিয়ে দিচ্ছি। যদি দেখি যে ইতিমধ্যে অনেক রিকোয়েস্ট জমে গেছে ভদ্রভাবে না করে দিচ্ছি। তারপর আবার ছিটকিনি বা মিউটেক্স নামিয়ে দিচ্ছি।

} // end of main

মেইন ফাংশন শেষ!

কোন থ্রেড যখন একটি রিকোয়েস্ট পায় তখন রিকোয়েস্ট হ্যান্ডলার নামে একটি ফাংশনের মাধ্যমে সেটি সামলায়। এখন আমরা সেই ফাংশনটি দেখব।

void *request_handler(void *arg)
{

রিকোয়েস্ট ‌হ্যান্ডলার শুরু হল।

int connfd;
char *reqBuff;
fflush(stdout);
//printf("\nThread %d starting", (int) arg);

এক আধটু ভ্যারিয়েবল সব জায়গাতেই লাগে!

for( ; ; )
{
   pthread_mutex_lock(&clntMutex);
   //printf("\nMutex locked by thread# %d.", (int) arg);
   while(clntGet == clntPut)
   pthread_cond_wait(&clntCondition, &clntMutex);

আবারও ইনফিনিটি লুপ ব্যবহার করছি, দু:খিত! আমি এখন শেয়ারড ভ্যারিয়েবলগুলো ব্যবহার করছি কাজেই মিউটেক্স বা ছিটকিনি লাগছে।

connfd = clntConnection[clntGet];
if(++clntGet == MAX_CLIENT)
clntGet = 0;

সার্ভার ওভারলোড হয়ে যাচ্ছে কিনা সেটাও খেয়াল রাখতে হচ্ছে।

// thread operation
// printf("\nIn between mutex lock-unlock. Thread# %d, Connection# %d", (int) arg, connfd);
reqBuff = (char *) malloc (1000);
read(connfd, reqBuff, 1000);

এক একটি রিকোয়েস্টের জন্য কি পরিমাণ রেম (RAM) খরচ হবে সেগুলো ঠিক করছি।

int temp;
temp = find_method(reqBuff);

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

if(temp == 0)
{
   //printf("\nfind_method returned 0");
   request[(int)arg] = parse_request(reqBuff);
   //printf("\nHost: %s\nFile Path: %s", request[(int)arg].hostName, request[(int)arg].filePath);
   reply[(int)arg] = prepare_reply(200, request[(int)arg]);
   printf("\n\nprinting the reply structure");
   //printf("\nCode: %d\nDate: %s\nServer: %s\nlast_modified: %s\ncontent_length: %ld\ncontent type: %s\nfile path: %s",   reply[(int)arg].code, reply[(int)arg].date, reply[(int)arg].server, reply[(int)arg].last_modified, reply[(int)arg].content_length, reply[(int)arg].content_type, reply[(int)arg].file_path);
   emit_reply(connfd, reply[(int)arg]);
}

এটি গেট মেথড তাই কাজে নেমে পড়ছি।

else if(temp == 1)
{
   printf("\nfind_method returned 1");
   reply[(int)arg] = prepare_reply(501, request[(int)arg]);
   emit_reply(connfd, reply[(int)arg]);
}

এটি পুট বা অন্য কোন স্বীকৃত মেথড যার সাপোর্ট আমি দিইনি। দু:খিত!

else
{
   printf("\nfind_method returned -1");
   reply[(int)arg] = prepare_reply(400, request[(int)arg]);
   emit_reply(connfd, reply[(int)arg]);
}

আজগুবি মেথড।

pthread_mutex_unlock(&clntMutex);
//printf("\nMutex unlocked by thread# %d.", (int) arg);
thrdPool[(int) arg].threadCount++;

ছিটকিনি!

   // shahriar bhai told that closing the connection formally is not so important
   // good performance achieved @ closing it formally
   close(connfd);
}

যখনই কোন রিসোর্স ব্যবহার করব না তখনই সেটি মেমোরি থেকে সরিয়ে নিব।

} // end of request_handler function

ফাংশন শেষ!

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

void emit_reply(int conn, Reply rply)
{

ফাংশন শুরু হল।

//pthread_mutex_lock(&emitMutex2);
if(rply.code == 200)
{

ইয়াহু! পেজটি সার্ভারে পাওয় গেছে!

char *ok_code = "HTTP/1.1 200 OK\r\n";
write(conn, ok_code, strlen(ok_code));

সুখবরের একটি কোড আছে। সেটি সেট করি।

char *date;
date = (char *)malloc(100);
strcpy(date, "Date: ");
strcat(date, rply.date);
strcat(date, "\r\n");
write(conn, date, strlen(date));

তারিখ সেট করি।

char *server;
server = (char *)malloc(100);
strcpy(server, "Server: ");
strcat(server, rply.server);
strcat(server, "\r\n");
write(conn, server, strlen(server));

কে উত্তর পাঠাচ্ছে সেটি জানাই।

char *last_modified;
last_modified = (char *)malloc(100);
strcpy(last_modified, "Last-Modified: ");
strcat(last_modified, rply.last_modified);
strcat(last_modified, "\r\n");
write(conn, last_modified, strlen(last_modified));

ওয়েবপেজটি সর্বশেষ কবে আপডেট করা হয়েছে তা জানাই।

char *content_length;
content_length = (char *)malloc(100);
strcpy(content_length, "Content-Length: ");
int decpnt, sign;
char *p;
p = (char *)malloc(20);
p = ecvt(rply.content_length, 15, &decpnt, &sign);
//printf("\nConversion-- %s %d %d", p, decpnt, sign);
 
strncat(content_length, p, decpnt);
strcat(content_length, "\r\n");
write(conn, content_length, strlen(content_length));

ওয়েবপেজ কত বাইট সেটি জানাই।

char *content_type;
content_type = (char *)malloc(100);
strcpy(content_type, "Content-Type: ");
strcat(content_type, rply.content_type);
strcat(content_type, "\r\n");
write(conn, content_type, strlen(content_type));

এটি ছবি, ভিডিও না শুধু লেখা সেই তথ্যটি জানাই।

write(conn, "\r\n", 2);
 
int b;
char c;
FILE *fp;
fp = (FILE *) malloc(sizeof(FILE));
fp = fopen(rply.file_path, "rb");
while(!feof(fp))
{
   c = getc(fp);
   if(!feof(fp))
   {
      //printf("%c", c);
      write(conn, &c, 1);
   }
}

এবার পেজটি পাঠাই।

//pthread_mutex_unlock(&emitMutex2);
return;
}

ফাংশন শেষ!

else if(rply.code == 400)
{
   char *ok_code = "HTTP/1.1 400 Bad Request\r\n";
   write(conn, ok_code, strlen(ok_code));
   
   char *date;
   date = (char *)malloc(100);
   strcpy(date, "Date: ");
   strcat(date, rply.date);
   strcat(date, "\r\n");
   write(conn, date, strlen(date));
   char *server;
   server = (char *)malloc(100);
   strcpy(server, "Server: ");
   strcat(server, rply.server);
   strcat(server, "\r\n");
   write(conn, server, strlen(server));
   write(conn, "\r\n", 2);
   
   char *error_400;
   error_400 = (char *)malloc(300);
   strcpy(error_400, "<html><head><title>Error No: 400. Bad Request.</title></head><body><h2>The server cannot parse the request.</h2><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><hr>For further info mail to shehab_sust@yahoo.com.</body><html>");
   write(conn, error_400, strlen(error_400));
}

যদি পেজটি পাওয়া না যায় তাহলে দু:সংবাদ জানিয়ে দেই।

else if(rply.code == 501)
{
   char *ok_code = "HTTP/1.1 501 Method Not Implemented\r\n";
   write(conn, ok_code, strlen(ok_code));

   char *date;
   date = (char *)malloc(100);
   strcpy(date, "Date: ");
   strcat(date, rply.date);
   strcat(date, "\r\n");
   write(conn, date, strlen(date));

   char *server;
   server = (char *)malloc(100);
   strcpy(server, "Server: ");
   strcat(server, rply.server);
   strcat(server, "\r\n");
   write(conn, server, strlen(server));
   write(conn, "\r\n", 2);

   char *error_501;
   error_501 = (char *)malloc(300);
   strcpy(error_501, "<html><head><title>Error No: 501. Method Not Implemented.</title></head><body><h2>The server only resolves the GET method.</h2><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><hr>For further info mail to shehab_sust@yahoo.com.</body><html>");
   write(conn, error_501, strlen(error_501));
}

এর মানে হল রিকোয়েস্টে কোন সমস্যা নেই কিন্তু যে মেথডে চাওয়া হয়েছে সেটির সাপোর্ট আমি এখনও দেই নি। দোষ আমারই।

else if(rply.code == 404)
{
   char *ok_code = "HTTP/1.1 404 File Not Found\r\n";
   write(conn, ok_code, strlen(ok_code));
   
   char *date;
   date = (char *)malloc(100);
   strcpy(date, "Date: ");
   strcat(date, rply.date);
   strcat(date, "\r\n");
   write(conn, date, strlen(date));

   char *server;
   server = (char *)malloc(100);
   strcpy(server, "Server: ");
   strcat(server, rply.server);
   strcat(server, "\r\n");
   write(conn, server, strlen(server));
   
   write(conn, "\r\n", 2);

   char *error_404;
   error_404 = (char *)malloc(300);
   strcpy(error_404, "<html><head><title>Error No: 404. File Not Found.</title></head><body><h2>The server could not found the requested url.</h2><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><hr>For further info mail to shehab_sust@yahoo.com.</body><html>");
   write(conn, error_404, strlen(error_404));
}

এর মানে হল ওয়েবসার্ভারে অনুরোধকৃত পেজের কোন অস্তিত্ত্বই নেই।

else if(rply.code == 403)
{
   char *ok_code = "HTTP/1.1 403 Forbidden\r\n";
   write(conn, ok_code, strlen(ok_code));

   char *date;
   date = (char *)malloc(100);
   strcpy(date, "Date: ");
   strcat(date, rply.date);
   strcat(date, "\r\n");
   write(conn, date, strlen(date));

   char *server;
   server = (char *)malloc(100);
   strcpy(server, "Server: ");
   strcat(server, rply.server);
   strcat(server, "\r\n");
   write(conn, server, strlen(server));

   write(conn, "\r\n", 2);

   char *error_403;
   error_403 = (char *)malloc(300);
   strcpy(error_403, "<html><head><title>Error No: 403. Forbidden.</title></head><body><h2>You are not authorized to access this url.</h2><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><hr>For further info mail to shehab_sust@yahoo.com.</body><html>");
 
   write(conn, error_403, strlen(error_403));
}

এর মানে হল পেজটি আছে কিন্তু দেখানো যাবেনা।

} //end of emit_reply

ফাংশন শেষ!

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

char *getMimeType(char *str)
{
   printf("\nentered mime type");
   int i;
   //defined here the file extension
   char *fileExt[MAX];
   fileExt[0] = "html";
   fileExt[1] = "htm";
   fileExt[2] = "txt";
   fileExt[3] = "gif";
   fileExt[4] = "jpg";
   fileExt[5] = "qt";

   //defined here the mime type
   char *mType[MAX];
   mType[0] = "text/html";
   mType[1] = "text/html";
   mType[2] = "text/plain";
   mType[3] = "image/gif";
   mType[4] = "image/jpeg";
   mType[5] = "video/quicktime";

   for(i = 0; i < 6; i++)
   {
      if(strcmp(str, fileExt[i]) == 0)
      {
         printf("\nmatched");
         return mType[i];
      }
   } //end of for loop
   printf("\nreturning ERROR");
   char *er = "ERROR";
   return er;
} //end of getMimetype

ফাংশনটির আইডিয়া খুব সহজ। রিকোয়েস্ট থেকে মাইমটাইপ পড়ে সে অনুযায়ী রিপ্লাইয়ের মাইমটাইপ ঠিক করে দিবে।

রিপ্লাই হিসেবে যে বাইটগুলো পাঠানো হয় সেগুলো গুছানোর জন্য প্রিপেয়ার রিপ্লাই নামে একটি ফাংশন ব্যবহার করা হয়।

Reply prepare_reply(int code, Request rq)
{

ফাংশন শুরু হল।

// extracting the file extension
char *drive, *direc, *fname, *ext;

drive = (char *) malloc (3);
direc = (char *) malloc (66);
fname = (char *) malloc (9);
ext = (char *) malloc (5);

Reply rpl;

শুরুতে মেমোরীতে কিছু জায়গা গুছিয়ে নেই।

if(code == 200)
{

এর মানে হল আমরা ওয়েবসাইট ভিজিটরকে জানাতে যাচ্ছে যে পেজটি সার্ভারে পাওয়া গেছে।

rpl.code = 200;
 
time_t lt;
lt = time(NULL);
strcpy(rpl.date, ctime(&lt));
rpl.date[strlen(rpl.date) - 1] = '\0';
 
strcpy(rpl.server, "TinyThreadedServer/0.1a");

কিছু খুচরো তথ্য গুছিয়ে নেই।

char dir[100];
getcwd(dir, 100);
//printf("\ncurrent dir is %s", dir);
//printf("\nfile path is %s", rq.filePath);
strcat(dir, "/");
strcat(dir, rq.filePath);
//printf("\nabsolute file path is %s", dir);
char *path, *p;
path = (char *) malloc (80);
p = (char *) malloc (80);
path = strtok(dir, "/");
strcpy(p, "/");
strcat(p, path);
do{
   path = strtok('\0', "/");
   if(path)
   {
      strcpy(ext, path);
      strcat(p, "/");
      strcat(p, path);
   }
} while(path);
//printf("\nthe c++ file path %s", p);

হার্ডডিস্কে কোথায় ফাইলটি আছে খুঁজে নেই।

char *e;
e = (char *)malloc(10);
e = strtok(ext, ".");
e = strtok('\0', ".");
//printf("\nfile extension is %s", e);

ফাইলের এক্সটেনশনটি জেনে নেই।

FILE *fp;
fp = (FILE *) malloc(sizeof(FILE));
if((fp = fopen(p, "r")) == NULL)
{
   printf("\nFILE NOT FOUND.\nERROR CODE 404");
   rpl.code = 404;
   return rpl;
}

ফাইলটি পড়া যায় কিনা দেখি।

struct stat buff;
stat(p, &buff);
//printf("\nSize: %ld\ntime of last modification: %s", buff.st_size, ctime(&buff.st_mtime));

strcpy(rpl.last_modified, ctime(&buff.st_mtime));
rpl.last_modified[strlen(rpl.last_modified) - 1] = '\0';

ফাইলটি সর্বশেষ কবে আপডেট করা হয়েছে সেই তথ্যটি টুকে নেই।

rpl.content_length = buff.st_size;

ফাইলের সাইজ কত সেই তথ্যটি নেই।

if(strcmp(getMimeType(e), "ERROR") == 0)
{
   rpl.code = 403;
   fclose(fp);
   return rpl;
}

ফাইলটি পড়ার পারমিশন আছে কিনা দেখি।

   strcpy(rpl.content_type, getMimeType(e));
   strcpy(rpl.file_path, p);
   fclose(fp);
}

আরো কিছু তথ্য নেই।

else if (code == 400)
{
   rpl.code = 400;

   time_t lt;
   lt = time(NULL);
   strcpy(rpl.date, ctime(&lt));
   rpl.date[strlen(rpl.date) - 1] = '\0';

   strcpy(rpl.server, "TinyThreadedServer/0.1a");
}

এর মানে হল ফাইলটি পাওয়া যায় নি।

else if (code == 501)
{
   rpl.code = 501;

   time_t lt;
   lt = time(NULL);
   strcpy(rpl.date, ctime(&lt));
   rpl.date[strlen(rpl.date) - 1] = '\0';
   
   strcpy(rpl.server, "TinyThreadedServer/0.1a");
}

এর মানে হল ফাইলটি আছে কিন্তু পড়ার পারমিশন নেই।

   return rpl;
} //end of prepare_reply

ফাংশন শেষ।

কোন ব্রাউজার যখন রিকোয়েস্ট পাঠায় তখন সেখানে অনেক ধরণের তথ্য থাকে। সেগুলো ধরে ধরে বুঝার জন্য পারস রিকোয়েস্ট ফাংশনটি ব্যবহার করা হয়।

Request parse_request(char *rbuff)
{
   char *fileName;
   char *hostName;
   int j, k, l, m, n;
   Request r;

ফাংশন শুরু হল।

fileName = (char *) malloc (100);
hostName = (char *) malloc (100);

মেমোরীতে একটু জায়গা নেই।

for(j = 5, k = 0; ;j++, k++)
{
   if(rbuff[j] == ' ') break;
   fileName[k] = rbuff[j];
} //end of for loop
fileName[k]= '\0';
//printf("\nThe valid file path is %s", fileName);
strcpy(r.filePath, fileName);

কোন ওয়েবপেজ দেখতে চায় সেটি জেনে নেই।

//finding the host
char *p;
p = strstr(rbuff, "Host: ");

for(l = 0; l < 6; l++)
{
   p++;
} //end of for loop

for(m = 0, n = 0; ; m++, n++)
{
   if(*p == '\r' || *p == '\n') break;
   hostName[n] = *p;
   p++;
} //end of for loop
hostName[n]= '\0';

fflush(stdout);
strcpy(r.hostName, hostName);

রিকোয়েস্টটি কোন কম্পিউটার থেকে এসেছে জেনে নেই।

   return r;
} // end of parse_request

ফাংশন শেষ।

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

int find_method (char *req)
{
   pthread_mutex_lock(&srvrMutex2);
   //printf("\nin the find_method function\n%s\nthe size of the request is: %d", req, strlen(req));

   char *method_list[6];
   method_list[0] = (char *) malloc (10);
   strcpy(method_list[0], "PUT");
   method_list[1] = (char *) malloc (10);
   strcpy(method_list[1], "HEAD");
   method_list[2] = (char *) malloc (10);
   strcpy(method_list[2], "POST");
   method_list[3] = (char *) malloc (10);
   strcpy(method_list[3], "DELETE");
   method_list[4] = (char *) malloc (10);
   strcpy(method_list[4], "TRACE");
   method_list[5] = (char *) malloc (10);
   strcpy(method_list[5], "CONNECT");
 
   int i, j = 0;
   char *r;
   r = (char *) malloc (10);
   for(i = 0; i < strlen(req); i++)
   {
      if(req[i] == ' ') break;
      r[i] = req[i];
   } //end of for loop
   r[i] = '\0';
   //printf("\nExtracted method is %s", r);

   if(strcmp(r, "GET") == 0)
   {
      //printf("\nit's a GET method !!!");
      pthread_mutex_unlock(&srvrMutex2);
      return 0;
   }
 
   for(i = 0; i < 7; i++)
   {
      if(strcmp(r, method_list[i]) == 0)
      {
         printf("\n%s matched with %s", r, method_list[i]);
         j = 1;
         break;
      }
   } //end of for loop

   if(j == 1)
   {
      pthread_mutex_unlock(&srvrMutex2);
      return 1;
   }
   else
   {
      pthread_mutex_unlock(&srvrMutex2);
      return -1;
   }
} // end of find_method

গেট মেথড হলে আমি আগাবো না হলে স্যরি বলে দিব।

কোড এখানেই শেষ! পড়ার জন্য ধন্যবাদ! এখন একটু মাথা খাটিয়ে বের করা আমার ওয়েব সার্ভারের সোর্স কোডের ফাইলের নাম allizom রেখেছি কেন?

ডেনিস রিচি – সি ও ইউনিক্সের জনক

আমরা যারা কম্পিউটার প্রোগ্রামিং করি , তাদের অনেকেরই প্রোগ্রামিং-এ হাতেখড়ি হয় সি প্রোগ্রামিং ভাষার মাধ্যমে। এই লেখায় আমরা জানবো সি প্রোগ্রামিং এর জনক ডেনিস রিচি সম্পর্কে।

ডেনিস রিচি ১৯৪১ সালের ৯ সেপ্টেম্বর নিউইয়র্কের ব্রনস্সভিল শহরে জন্মগ্রহণ করেন। তাঁর পুরো নাম ডেনিস  ম্যাকএলিস্টেয়ার রিচি। তাঁর নামের তিনটি অংশের আদ্যক্ষর নিয়ে তিনি ডিএমআর (DMR) নামের পরিচিত ছিলেন। তাঁর বাবা এলিস্টেয়ার ই. রিচি  ছিলেন বেল ল্যাবের একজন বিজ্ঞানী এবং সুইচিং সার্কিট তত্ত্বের বই “The Design of Switching Circuits”-এর সহ লেখক।

রিচির জন্ম নিউইয়র্কে হলেও তাঁর শৈশবেই তাঁর বাবা-মা নিউ জার্সিতে চলে যান। সেখানকার সামিট হাই স্কুল-এ (Summit High School)  তিনি পড়াশোনা করেন। সেখানকার পড়াশোনার পাঠ শেষ হবার পর তিনি স্নাতক ভর্তি হন জিনিয়াসদের সূতিকাগার হার্ভার্ড বিশ্ববিদ্যালয়ে। সেখানকার পদার্থবিজ্ঞান এবং ফলিত গণিত বিভাগ থেকে স্নাতক শেষ করার পর ১৯৬৭ সালে তিনি যোগদান করেন বেল রিসার্চ ল্যাব-এ। ১৯৬৮ সালে হার্ভার্ড বিশ্ববিদ্যালয় থেকে তিনি “Program Structure and Computational Complexity”  টপিকে প্যাট্রিক সি ফিশারের তত্ত্বাবধানে পিএইচডি ডিগ্রী লাভ করেন। যদিও তিনি অফিসিয়ালি তাঁর পিএইচডি ডিগ্রী কখনো গ্রহণ করেননি।

সি প্রোগ্রামিং ভাষার উদ্ভাবক এবং ইউনিক্স অপারেটিং সিস্টেমের শুরুর দিকের অবদানকারীদের মধ্যে ডেনিস রিচির নাম উল্লেখযোগ্য। তবে রিচি সবচেয়ে বেশি পরিচিত সি প্রোগ্রামিং ভাষার উপর রচিত বই “The C programming Language” বইয়ের সহ লেখক হিসেবে। এই বইয়ের লেখকদ্বয় কার্নিংহাম ও রিচি তাঁদের দুজনের নামের আদ্যক্ষর K&R দিয়েই সুপরিচিত।

সি প্রোগ্রামিং বইয়ের প্রথম সংস্করণের প্রচ্ছদ (ছবি : উইকি কমন্স)
সি প্রোগ্রামিং বইয়ের প্রথম সংস্করণের প্রচ্ছদ (ছবি : উইকি কমন্স)

সি প্রোগ্রামিং ভাষার উদ্ভাবন এবং কেন(Ken) থম্পসনের সঙ্গে ইউনিক্সের উন্নয়নে তাঁর অবদান রিচিকে আধুনিক কম্পিউটার বিজ্ঞানের অগ্রনায়ক হিসেবে চিহ্নিত করেছে। এপ্লিকেশন ও অপারেটিং সিস্টেম এবং এমবেডেড সিস্টেম উন্নয়নে ব্যবহার হওয়ার পাশাপাশি সি প্রোগ্রামিং ভাষা অন্যান্য প্রোগ্রামিং ভাষাকেও প্রবলভাবে প্রভাবিত করেছে। একই ভাবে আধুনিক কম্পিউটিং-এর বিকাশে ইউনিক্স সিস্টেমের অবদান অনস্বীকার্য।

Ken n dennis.jpg
কেন থমসনের সাথে ডেনিস রিচি (ডানে) (ছবি : উইকি কমন্স)

১৯৮৩ সালে রিচি এবং থম্পসন যৌথভাবে  টুরিং পুরস্কার লাভ করেন। তাঁদেরকে জেনেরিক অপারেটিং সিস্টেমের তত্ত্ব বিশেষ করে ইউনিক্সে তা প্রয়োগ করার কারণে” এই পুরস্কারের জন্য মনোনীত করা হয়। পুরস্কার প্রাপ্তিতে রিচি “রিফ্লেকশন অন সফটওয়্যার রিসার্চ (Reflections on Software Research)” নামে বক্তৃতা দেন।

সি প্রোগ্রামিং ভাষার উদ্ভাবন ও ইউনিক্সের উন্নয়নে অবদানের জন্য রিচি ১৯৮৮ সালে আমেরিকার ন্যাশনাল একাডেমি অব ইঞ্জিনিয়ারিং-এ নির্বাচিত হন। একই ক্ষেত্রে অবদানের জন্য রিচি এবং থম্পসন ১৯৯০ সালে ইনস্টিটিউট অফ ইলেকট্রিক্যাল অ্যান্ড ইলেকট্রনিক্স ইঞ্জিনিয়ার্স (IEEE) প্রবর্তিত আইইই রিসার্চ ডব্লিউ হামিং পদক লাভ করেন।

সি প্রোগ্রামিং ভাষা এবং উইনিক্স অপারেটিং উদ্ভাবনের মাধ্যমে কম্পিউটার হার্ডওয়্যার, সফটওয়্যার, নেটওয়ার্কিং, এবং সিমুলেশন ইত্যাদি ক্ষেত্রসমূহে বিশেষ অগ্রগতি লাভ করা সম্ভব হয় এবং এর মাধ্যমে তথ্যপ্রযুক্তির ক্ষেত্রে মার্কিন যুক্তরাষ্ট্র নেতৃত্ব দিতে থাকে। এই অবদানের জন্য খম্পসন ও রিচি ১৯৯৮ সালে আমেরিকার জাতীয় প্রযুক্তি পদকের (National Medal of Technology) জন্য নির্বাচিত হোন। ১৯৯৯ সালের ২১ এপ্রিল আমেরিকার তৎকালীন প্রেসিডেন্ট বিল ক্লিনটন তাঁদের হাতে এ পদক তুলে দেন।

২০০৫ সালে Industrial Research Institute ডেনিস রিচিকে তথ্য প্রযুক্তিতে অবদানের জন্য IRI Achievement Award এ ভূষিত করে।

ইউনিক্স অপারেটিং সিস্টেম উন্নয়নে অগ্রণী ভূমিকা পালনের জন্য ২০১১ সালে রিচি ও থম্পসন তথ্য ও যোগাযোগ প্রযুক্তিতে জাপান পুরস্কার লাভ করেন।

ডেনিস রিচি ২০১১ সালের ১২ অক্টোবর , বার্কলে হাউজ, নিউ জার্সিতে তাঁর নিজ বাসভবনে অনেকটা নীরবে এবং নিভৃতে মৃত্যুবরণ করেন। রব পিকে নামক তাঁর এক সহকর্মী তাঁর মৃত্যুর বিষয় প্রথম নিশ্চিত করেন। কিন্তু তার মৃত্যুর সঠিক সময় বা কারণ কোনোটিই প্রকাশ করা হয়নি। সংবাদ মাধ্যমেও এই কিংবদন্তী বিজ্ঞানীর মৃত্যুসংবাদ প্রচার হয় স্বল্পপরিসরে। ডেনিস রিচির মৃত্যুর পর  জেমস গ্রিমেল্ম্যান তাঁর টুইটার এ্যাকাউন্টে টুইট করে লেখেনঃ  “তাঁর পয়েন্টার শূন্যতে ছেড়ে দেয়া হয়েছে, তাঁর প্রসেসকে থামিয়ে দেয়া হয়েছে এক্সিট  কোড ০ দিয়ে।”

লেখক: তামান্না নিশাত রিনি

ইনফরমেটিক্স অলিম্পিয়াড

আন্তর্জাতিক ইনফরমেটিক্স অলিম্পিয়াডের লোগো

ইনফরমেটিক্স অলিম্পিয়াড এক ধরণের প্রোগ্রামিং প্রতিযোগিতা। এই প্রতিযোগিতায় স্কুল ও কলেজের শিক্ষার্থীরা অংশগ্রহন করতে পারে। ১৯৮৯ সালে বুলগেরিয়াতে প্রথমবারের মতো আন্তর্জাতিক ইনফরমেটিক্স অলিম্পিয়াড অনুষ্ঠিত হয়। তারপর থেকে প্রতি বছর এই প্রতিযোগিতা অনুষ্ঠিত হচ্ছে। বাংলাদেশও বিগত কয়েক বছর ধরে এই প্রতিযোগিতায় অংশ নিচ্ছে।

আন্তর্জাতিক ইনফরমেটিক্স অলিম্পিয়াডে দল গঠনের লক্ষ্যে প্রথমে জাতীয় ইনফরমেটিক্স অলিম্পিয়াড নামে প্রতিযোগিতা অনুষ্ঠিত হয়, তারপর সেখানে যারা ভালো করে, তাদেরকে নিয়ে বেশ কিছু বাছাই প্রতিযোগিতা করার পরে চূড়ান্ত দল গঠন করা হয়। আর জাতীয় প্রতিযোগিতায় আসতে হলে প্রথমে পাড়ি দিতে হবে বিভাগীয় অলিম্পিয়াড। বাংলাদেশ ইনফরমেটিক্স অলিম্পিয়াডের কোনো অফিশিয়াল ওয়েবসাইট নেই। তাই খবরাখবর পাওয়ার জন্য চোখ রাখতে হবে এই ফেসবুক গ্রুপে : https://www.facebook.com/groups/bdoifamily/

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

বিভাগীয় ইনফরমেটিক্স অলিম্পিয়াডে প্রস্তুতির জন্য হাই স্কুলের গণিত বইয়ের উপর পূর্ণ দখল থাকতে হবে। সেই সাথে ডক্টর কায়কোবাদ ও মুহম্মদ জাফর ইকবালের নিউরণে অনুরণন বইটি থেকে সমস্যা সমাধানের চেষ্টা করতে হবে। মুহম্মদ জাফর ইকবালের ‘গণিত এবং আরো গণিত’ ও একটি ভালো বই – ক্লাস সেভেন-এইটের শিক্ষার্থীরদের জন্য।

জাতীয় ইনফরমেটিক্স অলিম্পিয়াডে যেহেতু প্রোগ্রামিং করে সমস্যার সমাধান করতে হবে, তাই প্রোগ্রামিং জানতে হবে। প্রোগ্রামিং শেখার জন্য অনলাইনে একটি ফ্রি কোর্স আছে : http://programming-course.appspot.com/। একটি জিমেইল একাউন্ট থাকলেই রেজিস্ট্রেশন করা যায়। কোর্সের ভিডিওগুলো নিয়ে ‘প্রোগ্রামিংয়ে হাতে খড়ি’ নামক একটি ডিভিডিও তৈরি করা হয়েছে, যেটি পাওয়া যায় ঢাকার নীলক্ষেতের হক লাইব্রেরিতে। আর ঘরে বসেও কেনা যায়, রকমারি ডট কম থেকে। ভিডিও লেকচারের পাশাপাশি বই পড়তে হবে। প্রোগ্রামিং শেখা শুরুর করার জন্য বাংলায় ‘কম্পিউটার প্রোগ্রামিং ১ম খণ্ড (লেখক: তামিম শাহরিয়ার সুবিন, প্রকাশক: অন্যরকম প্রকাশনী)’ বইটি পড়া যেতে পারে। বইয়ের ওয়েবসাইটে বইটি বিনামূল্যে পড়া যায় : http://cpbook.subeen.com। এছাড়া ওয়েবসাইটে কিছু সমস্যা আছে, যেগুলো সমাধানের চেষ্টা করলে প্রোগ্রামিং স্কিল বাড়বে।

ইনফরমেটিক্স অলিম্পিয়াডের জন্য গাণিতিক জ্ঞান ও দক্ষতা বাড়াতে দ্বিমিক কম্পিউটিং স্কুল আয়োজিত, হাম্মাদ আলী স্যারের ডিসক্রিট ম্যাথ নামক অনলাইন কোর্সটি করা যেতে পারে এখান থেকে : http://discrete-math.appspot.com/। এটিও সবার জন্য ফ্রি। লেকচার ভিডিওগুলোর ডিভিডি কিনতে পাওয়া যায় ঢাকার নীলক্ষেতের হক লাইব্রেরি ও রকমারি ডট কম-এ।

প্রোগ্রামিং চর্চা করার জন্য আরো কিছু গুরুত্বপূর্ণ ওয়েবসাইটের লিঙ্ক পাওয়া যাবে এখানে : http://cpbook.subeen.com/2011/08/blog-post_854.html

সবার জন্য শুভকামনা।