কম্পিউটার টিউটোরিয়াল

রুবি WebSocket সার্ভার স্ক্র্যাচ থেকে বানান: ধাপে ধাপে গাইড

WebSocketjourdan اليومकल খুব চর্চা পাচ্ছে। আমরা শুনি এটা "ভবিষ্যত"। আমরা শুনি Rails 5-এ ActionCable-এর কারণে এখন এটি ব্যবহার করা অনেক সহজ। কিন্তু আসলে WebSocket কী? এটি কীভাবে কাজ করে?

এই পোস্টে আমরা এই প্রশ্নগুলোর উত্তর দেব একটি সাধারণ WebSocket সার্ভার স্ক্র্যাচ থেকে রুবিতে বানিয়ে। শেষে আমরা ব্রাউজার এবং আমাদের সার্ভারের মধ্যে দ্বিমুখী যোগাযোগ অর্জন করব।

এই পোস্টের কোড শুধু লার্নিং এক্সারসাইজ হিসেবে। আপনি যদি রিয়েল প্রোডাকশন অ্যাপে WebSocket ইমপ্লিমেন্ট করতে চান, তবে’excellent websocket-ruby gem’ চেক করুন। আপনি WebSocket Spec-ও দেখতে পারেন।

আপনি কখনো WebSocket শুনেননি

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

  • চ্যাটের মতো কিছু অ্যাপ্লিকেশনে, ফ্রন্ট এন্ডকে অবিলম্বে আপডেট হতে হয় নতুন মেসেজ আসা মাত্র। যদি আপনার पास শুধু নরমাল HTTP রিকোয়েস্ট থাকে, তবে সার্ভারকে συνεχে পোল করতে হয় নতুন কন্টেন্ট আছে কিনা দেখতে।
  • যদি আপনার ফ্রন্ট-এন্ড অ্যাপ্লিকেশন সার্ভারের কাছে অনেক ছোট রিকোয়েস্ট করে, তবে প্রতিটি রিকোয়েস্টের জন্য নতুন কানেকশন তৈরি করার ওভারহেড পারফরম্যান্স ইস্যু হতে পারে। HTTP2-এ এটি কম সমস্যা।

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

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

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

<!doctype html> <html lang="en"> <head> <title>Websocket Client</title> </head> <body> <script> var exampleSocket = new WebSocket("ws://localhost:2345"); exampleSocket.onopen = function (event) { exampleSocket.send("Can you hear me?"); }; exampleSocket.onmessage = function (event) { console.log(event.data); } </script> </body> </html> 

আমি যদি একটি ছোট স্ট্যাটিক সার্ভার চালু করি এবং এই ফাইলটি ব্রাউজারে খুলি, তবে একটি এরর পাব। সেটা স্বাভাবিক, কারণ সার্ভার এখনো নেই। আমাদের এখনো একটা বানাতে হবে। :-)

রুবি WebSocket সার্ভার স্ক্র্যাচ থেকে বানান: ধাপে ধাপে গাইড

সার্ভার শুরু করা

WebSocket শুরুতে নরমাল HTTP রিকোয়েস্ট হিসেবে শুরু হয়। এর একটি অদ্ভুত লাইফসাইকেল আছে:

  1. ব্রাউজার একটি নরমাল HTTP রিকোয়েস্ট পাঠায়, কিছু স্পেশাল হেডারসহ যেগুলো বলে "আমাকে একটি WebSocket বানাও।"
  2. সার্ভার একটি নির্দিষ্ট HTTP রেসপন্স দেয়, কিন্তু কানেকশন বন্ধ করে না।
  3. ব্রাউজার এবং সার্ভার তারপর একটি স্পেশাল WebSocket প্রোটোকল ব্যবহার করে খোলা কানেকশনের ওপর ডেটা ফ্রেম এক্সচেঞ্জ করে।

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

require 'socket' server = TCPServer.new('localhost', 2345) loop do # Wait for a connection socket = server.accept STDERR.puts "Incoming Request" # Read the HTTP request. We know it's finished when we see a line with nothing but \r\n http_request = "" while (line = socket.gets) && (line != "\r\n") http_request += line end STDERR.puts http_request socket.close end 

আমি সার্ভার রান করি, এবং আমার WebSocket টেস্ট পেজ রিফ্রেশ করি, তবে আমি এটি পাই:

$ ruby server1.rb Incoming Request GET / HTTP/1.1 Host: localhost:2345 Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: cG8zEwcrcLnEftn2qohdKQ== 

আপনি লক্ষ্য করলে দেখবেন, এই HTTP রিকোয়েস্টে WebSocket-রিলেটেড অনেক হেডার আছে। এটি আসলে WebSocket হ্যান্ডশেকের প্রথম ধাপ।

হ্যান্ডশেক

সব WebSocket রিকোয়েস্ট হ্যান্ডশেক দিয়ে শুরু হয়। এটি নিশ্চিত করার জন্য যে ক্লায়েন্ট এবং সার্ভার উভয়ই বুঝতে পারে WebSocket হচ্ছে এবং প্রোটোকল ভার্সনে সম্মত। এটি এভাবে কাজ করে:

ক্লায়েন্ট একটি HTTP রিকোয়েস্ট পাঠায় এইভাবে

GET / HTTP/1.1 Host: localhost:2345 Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: E4i4gDQc1XTIQcQxvf+ODA== Sec-WebSocket-Version: 13 

এই রিকোয়েস্টের সবচেয়ে গুরুত্বপূর্ণ অংশ হলো Sec-WebSocket-Key। ক্লায়েন্ট আশা করে সার্ভার এই ভ্যালুর একটি মডিফাইড ভার্সন রিটার্ন করবে প্রুফ হিসেবে XSS অট্যাক এবং ক্যাশিং প্রক্সির বিরুদ্ধে।

সার্ভার রেসপন্ড করে

HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: d9WHst60HtB4IvjOVevrexl0oLA= 

সার্ভার রেসপন্স বয়লারপ্লেট ছাড়া Sec-WebSocket-Accept হেডার ছাড়া। এই হেডার এইভাবে জেনারেট হয়:

# ক্লায়েন্ট দিয়ে দিয়ে ভ্যালু নিন, একটি ম্যাজিক স্ট্রিং অ্যাপেন্ড করুন। SHA1 হ্যাশ জেনারেট করুন, তারপর base64 এনকোড করুন। Digest::SHA1.base64digest([sec_websocket_accept, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"].join) 

আপনার চোখ আপনাকে ধোখা দিচ্ছে না। এখানে একটি ম্যাজিক কনস্ট্যান্ট থাকছে।

হ্যান্ডশেক ইমপ্লিমেন্ট করা

আমাদের সার্ভার আপডেট করি হ্যান্ডশেক কমপ্লিট করার জন্য। প্রথম, রিকোয়েস্ট হেডার থেকে সিকিউরিটি টোকেন বের করি:

# হেডার থেকে সিকিউরিটি কী বের করুন। # যদি না থাকে, তবে কানেকশন বন্ধ করুন। if matches = http_request.match(/^Sec-WebSocket-Key: (\S+)/) websocket_key = matches[1] STDERR.puts "Websocket handshake detected with key: #{ websocket_key }" else STDERR.puts "Aborting non-websocket connection" socket.close next end 

এখন, সিকিউরিটি কী ব্যবহার করে একটি বৈধ রেসপন্স জেনারেট করি:

response_key = Digest::SHA1.base64digest([websocket_key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"].join) STDERR.puts "Responding to handshake with key: #{ response_key }" socket.write <<-eos HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: #{ response_key } eos STDERR.puts "Handshake completed." 

যখন আমি WebSocket টেস্ট পেজ রিফ্রেশ করি, তবে দেখি আর কানেকশন এরর নেই। কানেকশন স্থাপিত হয়েছে!

রুবি WebSocket সার্ভার স্ক্র্যাচ থেকে বানান: ধাপে ধাপে গাইড

সার্ভার থেকে আউটপুট, সিকিউরিটি কী এবং রেসপন্স কী দেখাচ্ছে:

$ ruby server2.rb Incoming Request Websocket handshake detected with key: Fh06+WnoTQQiVnX5saeYMg== Responding to handshake with key: nJg1c2upAHixOmXz7kV2bJ2g/YQ= Handshake completed. 

WebSocket ফ্রেম প্রোটোকল

একবার WebSocket কানেকশন স্থাপিত হলে, HTTP আর ব্যবহার হয় না। বরং, ডেটা WebSocket প্রোটোকল দিয়ে এক্সচেঞ্জ হয়।

ফ্রেমগুলো WebSocket প্রোটোকলের বেসিক ইউনিট।

WebSocket প্রোটোকল ফ্রেম-ভিত্তিক। কিন্তু এটার মানে কী?

যখন আপনি আপনার ওয়েব ব্রাউজারকে WebSocket-এ ডেটা পাঠাতে বলেন, বা আপনার সার্ভারকে রেসপন্ড করতে বলেন, ডেটাটি টুকরো টুকরো ভাগ হয় এবং প্রতিটি চাঙ্ক কিছু মেটাডেটা দিয়ে র‍্যাপ হয় একটি ফ্রেম বানাতে।

ফ্রেম স্ট্রাকচারটি এভVéase। উপরের সংখ্যাগুলো বিট। এবং কিছু ফিল্ড, যেমন এক্সটেন্ডেড পেলোড লেন্থ, সবসময় উপস্থিত নাও থাকতে পারে:

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+ 

প্রথম যেটি আপনার চোখে পড়তে পারে সেটি হলো এটি একটি বাইনারি প্রোটোকল। আমাদের কিছু বিট ম্যানিপুলেশন করতে হবে, কিন্তু চিন্তা করবেন না — এটি আরো হার্ড হবে না। ফিগারের উপরের সংখ্যাগুলো বিট। এবং কিছু ফিল্ড সবসময় উপস্থিত নাও থাকতে পারে। উদাহরণস্বরূপ এক্সটেন্ডেড পেলোড লেন্থ উপস্থিত থাকবে যদি পেলোড ১২৭ বাইটের নিচে হয়।

ডেটা রিসিভ করা

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

বাইট ১: FIN এবং Opcode

উপরের টেবিল থেকে, আপনি দেখতে পাচ্ছেন যে প্রথম বাইট (প্রথম আট বিট) কিছু ডেটা ধারণ করে:

  • FIN: ১ বিট যদি এটি false হয়, তবে মেসেজ একাধিক ফ্রেমে ভাগ থাকে
  • opcode: ৪ বিট আমাদের বলে দেয় পেলোড টেক্সট, বাইনারি, বা এটি শুধু একটি "ping" কানেকশন আライブ রাখতে
  • RSV: ৩ বিট কারেন্ট WebSocket স্পেসিফিকেশনে এগুলো অনইউজড

প্রথম বাইট পেতে, আমরা IO#getbyte মেথড ব্যবহার করব। এবং ডেটা এক্সট্রাক্ট করতে, কিছু সিম্পল বিটমাস্কিং ব্যবহার করব। আপনি যদি বিটওয়াইজ অপারেটরের সাথে পরিচিত ন हों, তবে আমার অন্য আর্টিকেল "Bitwise hacks in Ruby" চেক করুন।

first_byte = socket.getbyte fin = first_byte & 0b10000000 opcode = first_byte & 0b00001111 # আমাদের সার্ভার শুধু সিঙ্গল-ফ্রেম, টেক্সট মেসেজ সাপোর্ট করবে। # এক্সসেপশন থ্রো করুন যদি ক্লায়েন্ট অন্য কিছু পাঠাতে চায়। raise "We don't support continuations" unless fin raise "We only support opcode 1" unless opcode == 1 

বাইট ২: MASK এবং পেলোড লেন্থ

ফ্রেমের দ্বিতীয় বাইটে পেলোড সম্পর্কে আরও ইনফোর্মেশন থাকে।

  • MASK: ১ বিট বুলিয়ান ফ্ল্যাগ ইন্ডিকেট করে পেলোড মাস্কড কিনা। যদি true হয়, তবে পেলোডটি ব্যবহার করার আগে "আনমাস্ক" করতে হবে। ক্লায়েন্ট থেকে আসা ফ্রেমের জন্য এটি সবসময় true হওয়া উচিত। স্পেসিফিকেশন বলে।
  • payload length: ৭ বিট যদি আমাদের পেলোড ১২৬ বাইটের কম হয়, তবে লেন্থ এখানে থাকে। যদি এই ভ্যালু ১২৬ এর বেশি হয়, তবে আরও বাইট ফলো করবে লেন্থ দিতে।

দ্বিতীয় বাইট আমরা এভাবে হ্যান্ডেল করি:

second_byte = socket.getbyte is_masked = second_byte & 0b10000000 payload_size = second_byte & 0b01111111 raise "All frames sent to a server should be masked according to the websocket spec" unless is_masked raise "We only support payloads < 126 bytes in length" unless payload_size < 126 STDERR.puts "Payload size: #{ payload_size } bytes" 

বাইট ৩-৭: মাস্কিং কী

আমরা আশা করি ইনকামিং সব ফ্রেমের পেলোড মাস্কড থাকবে। কন্টেন্ট আনমাস্ক করতে, আমাদের এটি XOR করতে হবে মাস্কিং কীর বিপরীতে।

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

mask = 4.times.map { socket.getbyte } STDERR.puts "Got mask: #{ mask.inspect }" 

আপনি যদি জানেন একটি ভালো উপায় ৪ বাইট অ্যারে-এ রিড করার, তবে বলুন। times.map একটু অস্বাভাবিক, কিন্তু এটি সবচেয়ে সংক্ষিপ্ত পদ্ধতি যা আমি চিন্তা করতে পারলাম। আমি @StarrHorne টুইটারে।

বাইট ৮ এবং তারপর: পেলোড

ঠিক আছে, মেটাডেটা শেষ। এখন আসল পেলোড ফেচ করি।

data = payload_size.times.map { socket.getbyte } STDERR.puts "Got masked data: #{ data.inspect }" 

মনে রাখবেন এই পেলোড মাস্কড। তাই আপনি যদি প্রিন্ট করেন, তবে এটি গার্বেজ দেখাবে। আনমাস্ক করতে, আমরা সিম্পলি প্রতিটি বাইটকে ماس্কের সংশ্লিষ্ট বাইটের সাথে XOR করব। যেহেতু মাস্ক শুধু চার বাইট, আমরা এটি রিপিট করব পেলোডের লেন্থের সাথে মেলাতে:

unmasked_data = data.each_with_index.map { |byte, i| byte ^ mask[i % 4] } STDERR.puts "Unmasked the data: #{ unmasked_data.inspect }" 

এখন আমাদের একটি বাইট অ্যারে আছে। এটিকে ইউনিকোড স্ট্রিং-এ কনভার্ট করতে হবে। WebSocket-এ সব টেক্সট ইউনিকোড।

STDERR.puts "Converted to a string: #{ unmasked_data.pack('C*').force_encoding('utf-8').inspect }" 

সব একসাথে করা

যখন আপনি এই সব কোড একসাথে করবেন, তবে আপনি একটি স্ক্রিপ্ট পাবেন যেটি এইরকম দেখাবে:

require 'socket' # Provides TCPServer and TCPSocket classes require 'digest/sha1' server = TCPServer.new('localhost', 2345) loop do # Wait for a connection socket = server.accept STDERR.puts "Incoming Request" # Read the HTTP request. We know it's finished when we see a line with nothing but \r\n http_request = "" while (line = socket.gets) && (line != "\r\n") http_request += line end # Grab the security key from the headers. If one isn't present, close the connection. if matches = http_request.match(/^Sec-WebSocket-Key: (\S+)/) websocket_key = matches[1] STDERR.puts "Websocket handshake detected with key: #{ websocket_key }" else STDERR.puts "Aborting non-websocket connection" socket.close next end response_key = Digest::SHA1.base64digest([websocket_key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"].join) STDERR.puts "Responding to handshake with key: #{ response_key }" socket.write <<-eos HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: #{ response_key } eos STDERR.puts "Handshake completed. Starting to parse the websocket frame." first_byte = socket.getbyte fin = first_byte & 0b10000000 opcode = first_byte & 0b00001111 raise "We don't support continuations" unless fin raise "We only support opcode 1" unless opcode == 1 second_byte = socket.getbyte is_masked = second_byte & 0b10000000 payload_size = second_byte & 0b01111111 raise "All incoming frames should be masked according to the websocket spec" unless is_masked raise "We only support payloads < 126 bytes in length" unless payload_size < 126 STDERR.puts "Payload size: #{ payload_size } bytes" mask = 4.times.map { socket.getbyte } STDERR.puts "Got mask: #{ mask.inspect }" data = payload_size.times.map { socket.getbyte } STDERR.puts "Got masked data: #{ data.inspect }" unmasked_data = data.each_with_index.map { |byte, i| byte ^ mask[i % 4] } STDERR.puts "Unmasked the data: #{ unmasked_data.inspect }" STDERR.puts "Converted to a string: #{ unmasked_data.pack('C*').force_encoding('utf-8').inspect }" socket.close end 

যখন আমি আমার WebSocket টেস্টার ওয়েবপেজ রিফ্রেশ করি এবং এটি আমার সার্ভারে রিকোয়েস্ট করে, তবে আমি এই আউটপুটটি দেখি:

$ ruby websocket_server.rb Incoming Request Websocket handshake detected with key: E4i4gDQc1XTIQcQxvf+ODA== Responding to handshake with key: d9WHst60HtB4IvjOVevrexl0oLA= Handshake completed. Starting to parse the websocket frame. Payload size: 16 bytes Got mask: [80, 191, 161, 254] Got masked data: [19, 222, 207, 222, 41, 208, 212, 222, 56, 218, 192, 140, 112, 210, 196, 193] Unmasked the data: [67, 97, 110, 32, 121, 111, 117, 32, 104, 101, 97, 114, 32, 109, 101, 63] Converted to a string: "Can you hear me?" 

ক্লায়েন্টের কাছে ডেটা পাঠানো

তাহলে আমরা সফলভাবে আমাদের ক্লায়েন্ট থেকে টয় WebSocket সার্ভারে একটি টেস্ট মেসেজ পাঠিয়েছি। এখন সার্ভার থেকে ক্লায়েন্টের কাছে একটি মেসেজ পাঠাতে পারলে কতটা কুল হতো।

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

যেভাবে আমরা ফ্রেমটি এক বাইট করে কনসিউম করেছিলাম, সেভাবেই আমরা এটি এক বাইট করে কনস্ট্রাক্ট করব।

বাইট ১: FIN এবং opcode

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

output = [0b10000001] 

বাইট ২: MASKED এবং পেলোড লেন্থ

যেহেতু এই ফ্রেম সার্ভার থেকে ক্লায়েন্টে যাচ্ছে, MASKED হবে শূন্য। অর্থাৎ আমরা এটা উপেক্ষা করতে পারি। পেলোড লেন্থ শুধু স্ট্রিং-এর লেন্থ।

output = [0b10000001, response.size] 

বাইট ৩ এবং তারপর: পেলোড

পেলোড মাস্কড নয়, এটি শুধু একটি স্ট্রিং।

response = "Loud and clear!" STDERR.puts "Sending response: #{ response.inspect }" output = [0b10000001, response.size, response] 

বমস্ অ্যাওয়ে!

এই পয়েন্টে, আমাদের একটি অ্যারে আছে যেটিতে আমরা যে ডেটা পাঠাতে চাই সেটি আছে। এটিকে একটি বাইট স্ট্রিং-এ কনভার্ট করতে হবে যেটি আমরা ওয়ায়ার-ওভার পাঠাতে পারি। এর জন্য আমরা সুপার-ভার্সেটাইল Array#pack মেথড ব্যবহার করব।

socket.write output.pack("CCA#{ response.size }") 

এই অদ্ভুত স্ট্রিং "CCA#{ response.size }" বলে Array#pack-কে যে অ্যারে-টিতে দুটি ৮-বিট আনসাইনড ইন্টিজার আছে, তারপর একটি ক্যারেক্টার স্ট্রিং নির্দিষ্ট সাইজের।

যদি আমি Chrome-এ নেটওয়ার্ক ইন্সপেক্টর খুলি, তবে দেখি মেসেজটি লাউড এবং ক্লিয়ার এসে পৌঁছেছে।

রুবি WebSocket সার্ভার স্ক্র্যাচ থেকে বানান: ধাপে ধাপে গাইড

এক্সট্রা ক্রেডিট

এটাই! আশা করি আপনি WebSocket সম্পর্কে কিছু শিখেছেন। সার্ভারে অনেক ফিচার মিসিং। আপনি যদি এক্সারসাইজ চালাতে চান, তবে এগুলো দেখতে পারেন:

  • মাল্টি-ফ্রেম পেলোড সাপোর্ট
  • বাইনারি পেলোড সাপোর্ট
  • Ping / Pong সাপোর্ট
  • লং পেলোড সাপোর্ট
  • ক্লোজিং হ্যান্ডশেক

  1. রুবিতে অ্যাপ্লিকেশন সার্ভার বুঝুন: কেন Puma গুরুত্বপূর্ণ

  2. মাস্টারিং ওয়েব স্ক্র্যাপিং: পডকাস্ট ডেটা এক্সট্র্যাকশনের ধাপে ধাপে গাইড – পার্ট ৩

  3. মাস্টারিং ওয়েব স্ক্র্যাপিং: আপনার প্রথম স্ক্র্যাপার বানান (পার্ট ৩) – পডকাস্ট ডেটা এক্সট্র্যাকশন

  4. ফ্যালাউট 76 'ডিসকানেক্টেড ফ্রম সার্ভার' এরর দ্রুত সমাধান করুন