পিএইচপি-তে কীভাবে ওয়েবসকেট সার্ভার তৈরি করবেন


89

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

#!/php -q
<?php  /*  >php -q server.php  */

error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();

$master  = WebSocket("localhost",12345);
$sockets = array($master);
$users   = array();
$debug   = false;

while(true){
  $changed = $sockets;
  socket_select($changed,$write=NULL,$except=NULL,NULL);
  foreach($changed as $socket){
    if($socket==$master){
      $client=socket_accept($master);
      if($client<0){ console("socket_accept() failed"); continue; }
      else{ connect($client); }
    }
    else{
      $bytes = @socket_recv($socket,$buffer,2048,0);
      if($bytes==0){ disconnect($socket); }
      else{
        $user = getuserbysocket($socket);
        if(!$user->handshake){ dohandshake($user,$buffer); }
        else{ process($user,$buffer); }
      }
    }
  }
}

//---------------------------------------------------------------
function process($user,$msg){
  $action = unwrap($msg);
  say("< ".$action);
  switch($action){
    case "hello" : send($user->socket,"hello human");                       break;
    case "hi"    : send($user->socket,"zup human");                         break;
    case "name"  : send($user->socket,"my name is Multivac, silly I know"); break;
    case "age"   : send($user->socket,"I am older than time itself");       break;
    case "date"  : send($user->socket,"today is ".date("Y.m.d"));           break;
    case "time"  : send($user->socket,"server time is ".date("H:i:s"));     break;
    case "thanks": send($user->socket,"you're welcome");                    break;
    case "bye"   : send($user->socket,"bye");                               break;
    default      : send($user->socket,$action." not understood");           break;
  }
}

function send($client,$msg){
  say("> ".$msg);
  $msg = wrap($msg);
  socket_write($client,$msg,strlen($msg));
}

function WebSocket($address,$port){
  $master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");
  socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");
  socket_bind($master, $address, $port)                    or die("socket_bind() failed");
  socket_listen($master,20)                                or die("socket_listen() failed");
  echo "Server Started : ".date('Y-m-d H:i:s')."\n";
  echo "Master socket  : ".$master."\n";
  echo "Listening on   : ".$address." port ".$port."\n\n";
  return $master;
}

function connect($socket){
  global $sockets,$users;
  $user = new User();
  $user->id = uniqid();
  $user->socket = $socket;
  array_push($users,$user);
  array_push($sockets,$socket);
  console($socket." CONNECTED!");
}

function disconnect($socket){
  global $sockets,$users;
  $found=null;
  $n=count($users);
  for($i=0;$i<$n;$i++){
    if($users[$i]->socket==$socket){ $found=$i; break; }
  }
  if(!is_null($found)){ array_splice($users,$found,1); }
  $index = array_search($socket,$sockets);
  socket_close($socket);
  console($socket." DISCONNECTED!");
  if($index>=0){ array_splice($sockets,$index,1); }
}

function dohandshake($user,$buffer){
  console("\nRequesting handshake...");
  console($buffer);
  //list($resource,$host,$origin,$strkey1,$strkey2,$data) 
  list($resource,$host,$u,$c,$key,$protocol,$version,$origin,$data) = getheaders($buffer);
  console("Handshaking...");

    $acceptkey = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
  $upgrade  = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $acceptkey\r\n";

  socket_write($user->socket,$upgrade,strlen($upgrade));
  $user->handshake=true;
  console($upgrade);
  console("Done handshaking...");
  return true;
}

function getheaders($req){
    $r=$h=$u=$c=$key=$protocol=$version=$o=$data=null;
    if(preg_match("/GET (.*) HTTP/"   ,$req,$match)){ $r=$match[1]; }
    if(preg_match("/Host: (.*)\r\n/"  ,$req,$match)){ $h=$match[1]; }
    if(preg_match("/Upgrade: (.*)\r\n/",$req,$match)){ $u=$match[1]; }
    if(preg_match("/Connection: (.*)\r\n/",$req,$match)){ $c=$match[1]; }
    if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)){ $key=$match[1]; }
    if(preg_match("/Sec-WebSocket-Protocol: (.*)\r\n/",$req,$match)){ $protocol=$match[1]; }
    if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/",$req,$match)){ $version=$match[1]; }
    if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }
    if(preg_match("/\r\n(.*?)\$/",$req,$match)){ $data=$match[1]; }
    return array($r,$h,$u,$c,$key,$protocol,$version,$o,$data);
}

function getuserbysocket($socket){
  global $users;
  $found=null;
  foreach($users as $user){
    if($user->socket==$socket){ $found=$user; break; }
  }
  return $found;
}

function     say($msg=""){ echo $msg."\n"; }
function    wrap($msg=""){ return chr(0).$msg.chr(255); }
function  unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); }
function console($msg=""){ global $debug; if($debug){ echo $msg."\n"; } }

class User{
  var $id;
  var $socket;
  var $handshake;
}

?>

এবং ক্লায়েন্ট:

var connection = new WebSocket('ws://localhost:12345');
connection.onopen = function () {
  connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
  console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
  console.log('Server: ' + e.data);
};

আমার কোডে কিছু ভুল থাকলে আপনি কি আমাকে এটি ঠিক করতে সহায়তা করতে পারেন? ফায়ারফক্সের কনকোল ডFirefox can't establish a connection to the server at ws://localhost:12345/.

সম্পাদনা করুন
যেহেতু এই প্রশ্নে অনেক আগ্রহ রয়েছে তাই আমি আপনাকে শেষ পর্যন্ত যা দিয়েছি তা সরবরাহ করার সিদ্ধান্ত নিয়েছি। এখানে আমার সম্পূর্ণ কোড।


4
এই পৃষ্ঠায় তালিকাভুক্ত করা হয়েছে যে তারাও বর্তমান পিএইচপিওউবসকেটগুলির সাথে সমস্যাগুলি নিয়েছিল এবং এসসিআর কোড উদাহরণগুলিতে তারা পরিবর্তনগুলি অন্তর্ভুক্ত করেছে: নেট.টুটস্প্লস
স্ক্র্যাপকোলা

4
ওয়েবসকেট অ্যাপ্লিকেশনগুলির জন্য ব্যবহারযোগ্য একটি দরকারী গ্রন্থাগার। ক্লায়েন্ট এবং পিএইচপি উভয় পক্ষ অন্তর্ভুক্ত করুন। techzonemind.com/…
জিতিন জোসে

4
আমি মনে করি এটি সি ++ এ প্রয়োগ করা ভাল।
মাইকেল চৌর্দাকিস

উত্তর:


118

আমি সম্প্রতি আপনার মতো একই নৌকায় ছিলাম এবং আমি যা করেছি তা এখানে:

  1. সার্ভার-সাইড কোডটি কীভাবে গঠন করা যায় তার জন্য আমি রেফারেন্স হিসাবে পিএইচপিওউবসকেট কোড ব্যবহার করেছি । (আপনি ইতিমধ্যে এটি করছেন বলে মনে হয় এবং আপনি উল্লেখ করেছেন যে কোডটি আসলে বিভিন্ন কারণে কার্যকর হয় না))

  2. আমি পিএইচপি.এন.এইচপিপিওয়েবসকেট কোডে ব্যবহৃত প্রতিটি সকেট ফাংশন সম্পর্কে বিশদ পড়তে ব্যবহার করেছি। এটি করে, আমি শেষ পর্যন্ত বুঝতে পারি যে কীভাবে পুরো সিস্টেমটি ধারণামূলকভাবে কাজ করে। এটি ছিল বেশ বড় বাধা।

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

  4. আমি # 3 এ খসড়ার নির্দেশাবলীর ভিত্তিতে যথাযথ হ্যান্ডশেক পদ্ধতিটি কোড করেছিলাম। এটি খুব খারাপ ছিল না।

  5. হ্যান্ডশেকের পরে আমি ক্লায়েন্টদের কাছ থেকে সার্ভারে প্রেরিত বেশ কয়েকটি পাঠ্য পাঠ্য রেখেছি এবং আমি কেন বুঝতে পারছিলাম না যতক্ষণ না বুঝেছি যে ডেটা এনকোড করা আছে এবং অবশ্যই তা আনমস্ক করা উচিত। নিম্নলিখিত লিঙ্কটি আমাকে এখানে অনেক সাহায্য করেছে: (মূল লিঙ্কটি ভাঙা) সংরক্ষণাগারযুক্ত অনুলিপি

    দয়া করে নোট করুন যে এই লিঙ্কটিতে উপলভ্য কোডটিতে বেশ কয়েকটি সমস্যা রয়েছে এবং পরবর্তী পরিবর্তন ছাড়া সঠিকভাবে কাজ করবে না।

  6. আমি নীচের এসও থ্রেড জুড়ে এসেছি, যা পরিষ্কারভাবে ব্যাখ্যা করে যে কীভাবে বার বার প্রেরণ করা হচ্ছে সঠিকভাবে এনকোড করা এবং ডিকোড করা যায়: আমি কীভাবে সার্ভার সাইডে ওয়েবস্কট বার্তাগুলি প্রেরণ এবং গ্রহণ করতে পারি?

    এই লিঙ্কটি সত্যই সহায়ক ছিল। ওয়েবসকেটের খসড়াটি দেখার সময় আমি এটির সাথে পরামর্শ করার পরামর্শ দিই। এটি খসড়া কী বলছে তা থেকে আরও সার্থক করে তুলতে সহায়তা করবে।

  7. আমি প্রায় এই মুহুর্তে সম্পন্ন হয়েছি, তবে ওয়েবস্কট ব্যবহার করে তৈরি করা একটি ওয়েবআরটিটিসি অ্যাপ্লিকেশনটির সাথে আমার কিছু সমস্যা ছিল, তাই আমি এসও-তে নিজের নিজের প্রশ্ন জিজ্ঞাসা করে শেষ করেছি, যা আমি শেষ পর্যন্ত সমাধান করেছি: ওয়েবআরটিটিসি পরীক্ষার্থীর তথ্যের শেষে এই ডেটাটি কী?

  8. এই মুহুর্তে, আমি প্রায় সব কাজ ছিল। সংযোগগুলি বন্ধ করার জন্য আমাকে কিছু অতিরিক্ত যুক্তি যুক্ত করতে হয়েছিল এবং আমার কাজ শেষ হয়ে গিয়েছিল।

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

শুভকামনা!


সম্পাদনা : এই সম্পাদনাটি আমার আসল উত্তরের কয়েক বছর পরে এবং আমার এখনও একটি কার্যনির্বাহী সমাধান থাকা সত্ত্বেও এটি ভাগ করে নেওয়ার জন্য সত্যিই প্রস্তুত নয়। ভাগ্যক্রমে, গিটহাবের অন্য কারও কাছে আমার কাছে প্রায় অভিন্ন কোড রয়েছে (তবে অনেক ক্লিনার), তাই আমি একটি কার্যনির্বাহী পিএইচপি ওয়েবস্কুট সমাধানের জন্য নিম্নলিখিত কোডটি ব্যবহার করার পরামর্শ দিচ্ছি:
https://github.com/ghedipunk/PHP-Wesbsockets/blob/master/ ওয়েবসকেট.এফপি


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


Thx, আমি এটি আপনার উপায়ে করার চেষ্টা করব। আপনি এই পিএইচপি সার্ভারের পারফরম্যান্সকে কীভাবে রেট করবেন?
ধর্মান

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

4
আমি এই মুহূর্তে এটি না, কিন্তু অদূর ভবিষ্যতে, আমি সমস্ত কোড সহ পুরো প্রক্রিয়া সম্পর্কে একটি টিউটোরিয়াল লেখার পরিকল্পনা করছি। আমি একবার এটি করার পরে, আমি লিঙ্ক পোস্ট করব।
হার্টলেসান

4
@ হার্টলেসান: হ্যালো! আমি আপনার কোডটি দেখতে খুব আগ্রহী হব আপনি এটি অনলাইনে রেখে দিতে পারেন, বা ব্যক্তিগতভাবে এটি আমার কাছে প্রেরণ করতে পারেন?
1313

হ্যাঁ, শীঘ্রই এটি অনলাইনে হবে। যার কাছে এটি চেয়েছে তাদের জন্য দুঃখিত। আমি সম্প্রতি খুব ব্যস্ত ছিলাম। আমি শীঘ্রই এটি পেতে হবে।
হার্টলেসান

26

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


4
আমি এখানে আমার সমাধানটি যুক্ত করেছি যা র্যাচেট এবং সাইলেক্স ব্যবহার করে: github.com/eole-io/sandstone আপনি জানেন না আপনি এটি কার্যকর
উপায়ে

8

কেন সকেট ব্যবহার করবেন না http://uk1.php.net/manual/en/book.sockets.php ? এটি ভাল নথিভুক্ত হয়েছে (কেবল পিএইচপি প্রসঙ্গে নয়) এবং এর ভাল উদাহরণ রয়েছে http://uk1.php.net/manual/en/sockets.exferences.php


4
হ্যাঁ আপনার প্লেইন পিএইচপি সকেট এবং একটি ওয়েব পৃষ্ঠার মধ্যে অবিচ্ছিন্ন সংযোগ থাকতে পারে, আমি এটি একাধিকবার পরীক্ষা করেছি।
WiMantis

@ উইম্যান্টিস: হ্যালো! আপনি কি এমন কোনও কোড উদাহরণ স্থাপন করতে পারেন যা এটি অনলাইনে করে, বা ব্যক্তিগতভাবে এটি আমার কাছে প্রেরণ করতে পারে?
1313

আপনি কি এটি নিয়মিত এইচটিটিপি সংযোগের পাশাপাশি ব্যবহার করতে পারেন? আমি একটি ডিডিডি কাঠামো তৈরি করছিলাম এবং আমি এর উপরে একটি র‌্যাপার ক্লাসের মতো তৈরি করতে চাই এবং সকেট কার্যকারিতা সরবরাহ করতে চাই, মূলত

1

বেস 64-এ কোডিংয়ের আগে কীটিকে হেক্স থেকে ডেকে রূপান্তর করতে হবে এবং তারপরে হ্যান্ডশেকের জন্য প্রেরণ করতে হবে।

$hashedKey = sha1($key. "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true);

$rawToken = "";
    for ($i = 0; $i < 20; $i++) {
      $rawToken .= chr(hexdec(substr($hashedKey,$i*2, 2)));
    }
$handshakeToken = base64_encode($rawToken) . "\r\n";

$handshakeResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $handshakeToken\r\n";

আমাকে জানতে দা্ও এটা সাহয্য করে কি - না।


1

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


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

কিরেন সিভা, সঠিক। কার্ল বা অনুরূপ, তারপরে নোড ওয়েবসকেটের মাধ্যমে একটি বার্তা সম্প্রচার করে
এমজেড

-2
<?php

// server.php

$server = stream_socket_server("tcp://127.0.0.1:8001", $errno, $errorMessage);

if($server == false) {
    throw new Exception("Could not bind to socket: $errorMessage");

}

for(;;) {
    $client = @stream_socket_accept($server);

    if($client) {
        stream_copy_to_stream($client, $client);
        fclose($client);
    }
}

এক টার্মিনাল রান থেকে: পিএইচপি সার্ভার.পিপি

অন্য একটি টার্মিনাল রান থেকে: প্রতিধ্বনি "হ্যালো ওয়ার্ল্ড" | এনসি 127.0.0.1 8002


4
তার মানে কী?
ধর্মমন

8
এটি সকেট, ওয়েবসকেট নয়। একটি বড় পার্থক্য আছে।
ক্রিস 21

@ টেকএক্সপান্ডার, প্রশ্ন অনুসারে আপনাকে প্রাক্তন পরিবর্তে স্ক্র্যাচ থেকে ব্যাখ্যা করতে হবে। যা কাজ করছে না।
জয়মিন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.