কম্পিউটার

রুবিতে স্ক্র্যাচ থেকে একটি সাধারণ ওয়েবসকেট সার্ভার তৈরি করা

Websockets এই দিন আরো এবং আরো প্রেস হচ্ছে. আমরা শুনেছি যে তারা "ভবিষ্যত"। আমরা শুনেছি যে এগুলো ব্যবহার করা আগের চেয়ে সহজ, ধন্যবাদ Rails 5-এ ActionCable-এর জন্য। কিন্তু ওয়েবসকেটগুলি আসলে কী? তারা কিভাবে কাজ করে?

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

এই পোস্টের কোড একটি শেখার ব্যায়াম হিসাবে বোঝানো হয়েছে. আপনি যদি একটি বাস্তব উত্পাদন অ্যাপে ওয়েবসকেটগুলি বাস্তবায়ন করতে চান তবে চমৎকার ওয়েবসকেট-রুবি রত্নটি দেখুন। আপনি WebSocket Spec এও কটাক্ষপাত করতে পারেন।

তাই আপনি কখনই ওয়েবসকেটের কথা শুনেননি

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

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

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

ক্লায়েন্ট পক্ষ

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

<!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>

আমি যদি একটু স্ট্যাটিক সার্ভার শুরু করি এবং আমার ওয়েব ব্রাউজারে এই ফাইলটি খুলি, আমি একটি ত্রুটি পাই। এটি অর্থপূর্ণ, কারণ এখনও কোন সার্ভার নেই। আমাদের এখনও একটি তৈরি করতে হবে। :-)

রুবিতে স্ক্র্যাচ থেকে একটি সাধারণ ওয়েবসকেট সার্ভার তৈরি করা

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

ওয়েব সকেট স্বাভাবিক HTTP অনুরোধ হিসাবে জীবন শুরু. তাদের একটি অদ্ভুত জীবনচক্র আছে:

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

তাই আমাদের জন্য প্রথম ধাপ হল একটি ওয়েব সার্ভার তৈরি করা। নীচের কোডে, আমি সবচেয়ে সহজ সম্ভাব্য ওয়েব সার্ভার তৈরি করছি। এটি আসলে কিছুই পরিবেশন করে না। এটি কেবল একটি অনুরোধের জন্য অপেক্ষা করে তারপর এটি 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

যদি আমি সার্ভার চালাই, এবং আমার ওয়েবসকেট পরীক্ষার পৃষ্ঠা রিফ্রেশ করি, আমি এটি পাব:

$ 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 অনুরোধটিতে একগুচ্ছ হেডার রয়েছে যা ওয়েব সকেটের সাথে সম্পর্কিত। এটি আসলে ওয়েবসকেট হ্যান্ডশেকের প্রথম ধাপ

হ্যান্ডশেক

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

ক্লায়েন্ট এভাবে একটি 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 ছাড়া সার্ভারের প্রতিক্রিয়া হল বয়লারপ্লেট হেডার এই শিরোনামটি এভাবে তৈরি করা হয়েছে:

# Take the value provided by the client, append a magic
# string to it. Generate the SHA1 hash, then base64 encode it.
Digest::SHA1.base64digest([sec_websocket_accept, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"].join)

তোমার চোখ মিথ্যে নয়। একটি যাদু ধ্রুবক জড়িত আছে.

হ্যান্ডশেক বাস্তবায়ন করা

হ্যান্ডশেক সম্পূর্ণ করতে আমাদের সার্ভার আপডেট করা যাক। প্রথমত, আমরা অনুরোধ শিরোনাম থেকে নিরাপত্তা টোকেন বের করে আনব:

# 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."

যখন আমি ওয়েবসকেট পরীক্ষার পৃষ্ঠাটি রিফ্রেশ করি, তখন আমি এখন দেখতে পাচ্ছি যে আর কোনও সংযোগ ত্রুটি নেই। সংযোগ স্থাপন করা হয়েছে!

রুবিতে স্ক্র্যাচ থেকে একটি সাধারণ ওয়েবসকেট সার্ভার তৈরি করা

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

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

ওয়েবসকেট ফ্রেম প্রোটোকল

একবার একটি WebSocket সংযোগ প্রতিষ্ঠিত হলে, HTTP আর ব্যবহার করা হয় না। পরিবর্তে, ওয়েবসকেট প্রোটোকলের মাধ্যমে ডেটা আদান-প্রদান করা হয়।

ফ্রেম হল WebSocket প্রোটোকলের মৌলিক একক।

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

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

ফ্রেম গঠন দেখতে কেমন তা এখানে। শীর্ষ বরাবর সংখ্যা বিট হয়. এবং কিছু ক্ষেত্র, যেমন বর্ধিত পেলোড দৈর্ঘ্য সবসময় উপস্থিত নাও থাকতে পারে:

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 ...                |
+---------------------------------------------------------------+

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

ডেটা গ্রহণ করা

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

বাইট 1:FIN এবং Opcode

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

  • FIN:1 বিট যদি এটি মিথ্যা হয়, তাহলে বার্তাটি একাধিক ফ্রেমে বিভক্ত হয়
  • opcode:4 বিট পেলোডটি পাঠ্য, বাইনারি, অথবা সংযোগটিকে বাঁচিয়ে রাখার জন্য এটি একটি "পিং" কিনা তা আমাদের জানান৷
  • RSV:3 বিট বর্তমান WebSockets স্পেসে এগুলি অব্যবহৃত৷

প্রথম বাইট পেতে, আমরা IO#getbyte ব্যবহার করব পদ্ধতি এবং ডেটা বের করতে, আমরা কিছু সাধারণ বিটমাস্কিং ব্যবহার করব। আপনি যদি বিটওয়াইজ অপারেটরদের সাথে পরিচিত না হন তবে রুবিতে আমার অন্য নিবন্ধটি দেখুন বিটওয়াইজ হ্যাকস

first_byte = socket.getbyte
fin = first_byte & 0b10000000
opcode = first_byte & 0b00001111

# Our server will only support single-frame, text messages.
# Raise an exception if the client tries to send anything else.
raise "We don't support continuations" unless fin
raise "We only support opcode 1" unless opcode == 1

বাইট 2:MASK এবং পেলোডের দৈর্ঘ্য

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

  • মাস্ক:১ বিট বুলিয়ান পতাকা নির্দেশ করে যে পেলোডটি মাস্ক করা হয়েছে কিনা। যদি এটি সত্য হয়, তাহলে পেলোডটি ব্যবহারের আগে "আনমাস্কড" হতে হবে। আমাদের ক্লায়েন্ট থেকে আসা ফ্রেমের জন্য এটি সর্বদা সত্য হওয়া উচিত। বিশেষত্ব তাই বলে।
  • পেলোডের দৈর্ঘ্য:৭ বিট আমাদের পেলোড 126 বাইটের কম হলে, দৈর্ঘ্য এখানে সংরক্ষণ করা হয়। যদি এই মানটি 126-এর বেশি হয়, তার মানে আমাদের দৈর্ঘ্য দিতে আরও বাইট অনুসরণ করবে।

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

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"

বাইট 3-7:মাস্কিং কী

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

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

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

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

বাইট 8 এবং তার বেশি:পেলোড

ঠিক আছে, আমরা মেটাডেটা দিয়ে শেষ করেছি। এখন প্রকৃত পেলোড আনতে পারেন৷

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 }"

এখন আমরা বাইট একটি অ্যারে আছে. আমাদের এটিকে ইউনিকোড স্ট্রিং-এ রূপান্তর করতে হবে। ওয়েবসকেটের সমস্ত পাঠ্যই ইউনিকোড৷

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 সার্ভারে একটি পরীক্ষামূলক বার্তা পাঠিয়েছি। এখন সার্ভার থেকে ক্লায়েন্টকে একটি বার্তা ফেরত পাঠানো ভালো হবে৷

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

ঠিক যেমন আমরা একবারে এক বাইট ফ্রেম ব্যবহার করি, আমরা একে একে একে বাইট তৈরি করতে যাচ্ছি।

বাইট 1:FIN এবং opcode

আমাদের পেলোড একটি ফ্রেমে মাপসই করা যাচ্ছে, এবং এটি টেক্সট হতে যাচ্ছে. এর মানে হল যে FIN 1 সমান হবে, এবং অপকোডও একটি সমান হবে। যখন আমি একই বিট ফরম্যাট ব্যবহার করে যারা একত্রিত করি যা আমরা আগে ব্যবহার করেছি, আমি একটি সংখ্যা পাই:

output = [0b10000001]

বাইট 2:মাস্কড এবং পেলোড দৈর্ঘ্য

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

output = [0b10000001, response.size]

বাইট 3 এবং তার বেশি:পেলোড

পেলোডটি মুখোশযুক্ত নয়, এটি কেবল একটি স্ট্রিং৷

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 বলে যে অ্যারেটিতে দুটি 8-বিট স্বাক্ষরবিহীন ইনট রয়েছে, তারপরে নির্দিষ্ট আকারের একটি অক্ষর স্ট্রিং রয়েছে৷

যদি আমি ক্রোমে নেটওয়ার্ক ইন্সপেক্টর খুলি, আমি দেখতে পাব যে বার্তাটি উচ্চস্বরে এবং স্পষ্টভাবে এসেছে৷

রুবিতে স্ক্র্যাচ থেকে একটি সাধারণ ওয়েবসকেট সার্ভার তৈরি করা

অতিরিক্ত ক্রেডিট

এটাই! আমি আশা করি আপনি WebSockets সম্পর্কে কিছু শিখেছেন। সার্ভার অনুপস্থিত অনেক জিনিস আছে. আপনি যদি ব্যায়াম চালিয়ে যেতে চান তবে আপনি সেগুলি দেখতে পারেন:

  • মাল্টি-ফ্রেম পেলোডের জন্য সমর্থন
  • বাইনারী পেলোড সমর্থন
  • পিং / পং সমর্থন
  • দীর্ঘ পেলোড সমর্থন
  • হ্যান্ডশেক বন্ধ করা

  1. কেন আমাদের রুবিতে অ্যাপ্লিকেশন সার্ভারের প্রয়োজন? (পুমার মত)

  2. আপনার প্রথম ওয়েব স্ক্র্যাপার তৈরি করা, পার্ট 3

  3. আপনার প্রথম ওয়েব স্ক্র্যাপার তৈরি করা, পার্ট 3

  4. সার্ভার থেকে সংযোগ বিচ্ছিন্ন ফলআউট 76 ঠিক করুন