GO-সকেট প্রোগ্রামিং

সকেট কি? এটা কি বিদ্যুতের সকেট নাকি?

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

আমরা যে ওয়েবসাইট, বিভিন্ন FTP, ইমেইল ক্লায়েন্ট ব্যবহার করি এগুলি সবই সকেট ব্যবহার করে। খুব গুরুত্বপূর্ন তাই না?

বাস্তবে না থাকলে সকেট কিভাবে পাই?

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

নেটওয়ার্কিং লেয়ারে সকেট

নেটওয়ার্কিং লেয়ারের বিভিন্ন এড্রেসিং স্কিম এর দিকে তাকালে আমরা দেখতে পাবো ট্রান্সপোর্ট লেয়ার "পোর্ট এড্রেসিং" ব্যবহার করে।

পোর্ট কি?

পোর্ট হলো একটা কম্পিউটার বা নেটওয়ার্ক সাপোর্টেড ডিভাইসে রানিং হওয়া কোনো প্রসেসকে অন্য ডিভাইস থেকে নেটওয়ার্কের মাধ্যমে এক্সেস করার একটা পথ। অনেকটা জাহাজের পোর্টের মত।

অপারেটিং সিস্টেম তার হোস্ট ডিভাইসের আইপি এড্রেস ও কোনো রানিং প্রসেসের পোর্ট এড্রেসের সমন্বয়ে একটা সকেট এড্রেস তৈরি করে যার মাধ্যমে তথ্য আদান-প্রদান করা যাবে।

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

সকেট প্রোগ্রামিং এর ক্লায়েন্ট-সার্ভার মডেল

উপরে আমরা যে socket(), connect(), close() ইত্যাদি ফাংশন দেখতে পাচ্ছি এগুলি হলো অপারেটিং সিস্টেমের API কল, যাকে System Call বা Syscall বলে।

যাক এই পর্যন্ত তো আমরা বুঝেছি তাই না? এবার আসুন আমরা সকেট এর মাধ্যমে ক্লায়েন্ট সার্ভার এপ্লিকেশন বানাই, আমরা উপরের স্টেট ডায়াগ্রামটি ফলো করবো

সকেট সার্ভার

package main

import (
"net"
"fmt"
)

func main() {
lis, err := net.Listen("tcp", "127.0.0.1:8080")
defer lis.Close()

if err != nil {
fmt.Println(err)
}

for {
conn, err := lis.Accept()
if err != nil {
fmt.Println(err)
}

fmt.Println("New Connection")

data := "Hello World"
conn.Write([]byte(data))
conn.Close()
}
}

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

প্রথমে আমরা একই সাথে ৩ টি কাজ এক লাইনে করেছি

lis, err := net.Listen("tcp", "127.0.0.1:8080")

০১ সকেট বানিয়েছি

০২ এড্রেসে বাইন্ড করেছি

০৩ লিসেনার পেয়েছি

আমরা TCP প্রোটোকলের মাধ্যমে “127.0.0.1:8080” এই এড্রেসে একটা সকেট বানিয়েছি, যার ফলে আমরা একটি লিসেনার পেয়েছি। এই লিসেনার দিয়ে আমরা পরের কাজগুলি করবো। এই এড্রেসে আমাদের সকেট Server এপ্লিকেশনটি রানিং আছে

সার্ভার সাধারণত সব সময় সকেট চালূ করে বসে থাকে, যার জন্য একে Long Live Connection বলে। অপরদিকে ক্লায়েন্ট সাধারণত Short Live হয়, মানে একবার কাজ করেই কানেকশন ক্লোজ করে দিতে হয়।

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

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

ট্রান্সপোর্ট লেয়ারে সকেটের মাধ্যমে শুধুমাত্র বাইট ফরমেটেই ডাটা আদান-প্রদান হয়, যাকে বলে বাইট স্ট্রিম। তাই আমরা ক্লায়েন্টকে Hello World মেসেজ পাঠাতে গিয়ে একে বাইট স্ট্রিম এ রূপান্তর করে পাঠিয়েছি।

যাই হোক, সার্ভার রান করলেই আমাদের সকেট সার্ভারের কানেকশন উন্মুক্ত হবে ক্লায়েন্টকে গ্রহণ করার জন্য।

সকেট ক্লায়েন্ট

আমরা চাইলে প্রোগ্রামিং করেই ক্লায়েন্ট বানাতে পারি। এর চেয়ে আমরা আগে থেকেই দিয়ে দেয়া অপারেটিং সিস্টেমের ক্লায়েন্টকে ব্যবহার করতে পারি এখন।

telnet দিয়ে আমরা খুব সহজেই TCP Socket রিক্যুয়েস্ট পাঠাতে পারি সার্ভারে। এর জন্য প্রথমেই telnet ইন্সটল করে নিন। এবার নিচের মত করে সার্ভারে রিক্যুয়েস্ট পাঠাই

telnet 127.0.0.1 8080

আমরা নিচের মত একটা রেসপন্স পাবো

দেখুন, আমরা এখানে সার্ভার থেকে Hello World যেটা পাঠিয়েছিলাম তা পেয়েছি। তার নিচেই দেখুন লেখা আছে Connection to host lost, এর কারণ হলো ক্লায়েন্ট এর কাজ করা শেষে আমরা ক্লায়েন্টকে বিদায় করে দিয়েছি close() সিসকলের মাধ্যমে।

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

আমরা চাইলেই একে কনকারেন্ট সার্ভার বা নন-ব্লকিং বানাতে পারি Goroutine এর সাহায্যে। আসুন তাহলে দেখি

package main

import (
"net"
"fmt"
)

func main() {
lis, err := net.Listen("tcp", "127.0.0.1:8081")
defer lis.Close()

if err != nil {
fmt.Println(err)
}

for {
conn, err := lis.Accept()
if err != nil {
fmt.Println(err)
}

fmt.Println("New Connection")

go handler(conn)
}
}

func handler(conn net.Conn) {
for {
data := "Hello World"
conn.Write([]byte(data))
}
conn.Close()
}

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

Conclusion

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

আল্লাহ হাফেয

Cyan Tarek

Backend Software Engineer(Golang) @ Elo

--

--

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

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