कैसे PHP में websockets सर्वर बनाने के लिए


89

क्या कोई ट्यूटोरियल या गाइड हैं जो दिखाते हैं कि कैसे अपने आप को PHP में एक साधारण वेबसोकेट सर्वर लिखना है? मैंने इसे Google पर ढूंढने की कोशिश की है, लेकिन मुझे कई नहीं मिले। मुझे phpwebsockets मिला लेकिन यह अभी पुराना है और नवीनतम प्रोटोकॉल का समर्थन नहीं करता है। मैंने इसे स्वयं अपडेट करने की कोशिश की, लेकिन यह काम नहीं करता है।

#!/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);
};

अगर मेरे कोड में कुछ भी गलत है तो क्या आप मुझे इसे ठीक करने में मदद कर सकते हैं? फ़ायरफ़ॉक्स में Concole कहते हैंFirefox can't establish a connection to the server at ws://localhost:12345/.

EDIT
चूंकि इस सवाल में बहुत रुचि है, मैंने आपको वही प्रदान करने का फैसला किया जो मैं आखिर में आया था। यहाँ मेरा पूरा कोड है।


1
यह पृष्ठ सूचीबद्ध करता है कि उनके पास भी वर्तमान phpwebsockets के साथ समस्याएँ थीं और इसमें वे परिवर्तन शामिल हैं जो उन्होंने उदाहरणों में दिए गए हैं src कोड: net.tutsplus.com/tutorials/javascript-ajax/…
scrappedcola

1
एक उपयोगी पुस्तकालय जो वेबसॉकेट अनुप्रयोगों के लिए उपयोग किया जा सकता है। क्लाइंट और पीएचपी दोनों को शामिल करें। techzonemind.com/…
जोस

1
मुझे लगता है कि इसे C ++ में लागू करना बेहतर है।
माइकल चाउरडकिस

जवाबों:


119

मैं हाल ही में उसी नाव में था, और यहाँ मैंने किया है:

  1. सर्वर-साइड कोड को कैसे तैयार किया जाए, इसके संदर्भ के रूप में मैंने phpwebsockets कोड का उपयोग किया । (आप पहले से ही ऐसा कर रहे हैं, और जैसा कि आपने उल्लेख किया है, कोड वास्तव में कई कारणों से काम नहीं करता है।)

  2. मैंने phpwebsockets कोड में उपयोग किए जाने वाले प्रत्येक सॉकेट फ़ंक्शन के बारे में विवरण पढ़ने के लिए PHP.net का उपयोग किया। ऐसा करने से, मैं अंत में यह समझने में सक्षम था कि पूरी प्रणाली वैचारिक रूप से कैसे काम करती है। यह एक बहुत बड़ी बाधा थी।

  3. मैंने वास्तविक WebSocket ड्राफ्ट को पढ़ा । मुझे इस चीज़ को बार-बार पढ़ने से पहले अंत में डूबना पड़ता था। आपको इस दस्तावेज़ को फिर से पूरी प्रक्रिया में वापस जाना होगा, क्योंकि यह सही, अप-टू-डेट वाला एक निश्चित संसाधन है WebSocket API के बारे में जानकारी।

  4. मैंने # 3 में मसौदे के निर्देशों के आधार पर उचित हैंडशेक प्रक्रिया को कोडित किया। यह बहुत बुरा नहीं था।

  5. मैं ग्राहकों से हैंडशेक के बाद सर्वर से भेजे गए गार्बल्ड टेक्स्ट का एक गुच्छा प्राप्त करता रहा और मैं यह पता नहीं लगा सका कि जब तक मुझे एहसास नहीं हुआ कि डेटा एनकोडेड है और अनमास्क होना चाहिए। निम्नलिखित लिंक ने मुझे यहाँ बहुत मदद की:मूल लिंक टूट गया) संग्रहीत प्रति

    कृपया ध्यान दें कि इस लिंक पर उपलब्ध कोड में कई समस्याएं हैं और आगे संशोधन के बिना यह ठीक से काम नहीं करेगा।

  6. मैं फिर निम्नलिखित SO थ्रेड में आया, जो स्पष्ट रूप से बताता है कि संदेशों को सही तरीके से कैसे एनकोड और डिकोड किया जा सकता है : मैं सर्वर साइड पर WebSocket मैसेज कैसे भेज और प्राप्त कर सकता हूं?

    यह लिंक वास्तव में मददगार था। मैं WebSocket ड्राफ्ट को देखते हुए इसे सलाह देने की सलाह देता हूं। यह ड्राफ्ट जो कह रहा है उससे अधिक समझ बनाने में मदद करेगा।

  7. मैं लगभग इस बिंदु पर किया गया था, लेकिन कुछ मुद्दों पर एक WebRTC ऐप था जिसे मैं WebSocket का उपयोग कर बना रहा था, इसलिए मैंने SO पर अपना प्रश्न पूछना समाप्त कर दिया, जिसे मैंने अंततः हल कर दिया: WebRTC उम्मीदवार जानकारी के अंत में यह डेटा क्या है?

  8. इस बिंदु पर, मैं बहुत ज्यादा यह सब काम कर रहा था। मुझे बस कनेक्शन बंद करने से निपटने के लिए कुछ अतिरिक्त तर्क जोड़ने थे, और मैं किया गया था।

उस प्रक्रिया में मुझे कुल दो सप्ताह लगे। अच्छी खबर यह है कि मैं समझता हूं कि वेबसॉकेट अब वास्तव में अच्छी तरह से समझ गया है और मैं अपने क्लाइंट और सर्वर स्क्रिप्ट को स्क्रैच से बनाने में सक्षम था जो बहुत अच्छा काम करता है। उम्मीद है कि सभी जानकारी की परिणति आपको अपने स्वयं के WebSocket PHP स्क्रिप्ट को कोड करने के लिए पर्याप्त मार्गदर्शन और जानकारी देगी।

सौभाग्य!


संपादित करें : यह संपादन मेरे मूल उत्तर के कुछ साल बाद का है, और जब मेरे पास अभी भी एक कार्यशील समाधान है, तो यह वास्तव में साझा करने के लिए तैयार नहीं है। सौभाग्य से, GitHub पर किसी और के पास मेरा (लेकिन बहुत क्लीनर) के समान कोड है, इसलिए मैं एक काम करने वाले PHP WebSocket समाधान के लिए निम्नलिखित कोड का उपयोग करने की सलाह देता हूं:
https://github.com/ghedipunk/PHP-Websockets-blob/master/ websockets.php


# 2 संपादित करें : जबकि मैं अभी भी बहुत से सर्वर-साइड संबंधित चीजों के लिए PHP का उपयोग करने का आनंद लेता हूं, मुझे यह स्वीकार करना होगा कि मैंने हाल ही में Node.js को बहुत गर्म किया है, और मुख्य कारण यह है कि यह बेहतर डिज़ाइन किया गया है। PHP (या किसी अन्य सर्वर-साइड भाषा) की तुलना में WebSocket को संभालने के लिए जमीन। जैसे, मैंने हाल ही में पाया है कि Apache / PHP और Node.js दोनों को अपने सर्वर पर सेट करना बहुत आसान है और WebSocket सर्वर और Apache / PHP सब कुछ के लिए चलाने के लिए Node.js का उपयोग करें। और उस मामले में जहां आप एक साझा होस्टिंग वातावरण पर हैं जिसमें आप WebSocket के लिए Node.js स्थापित / उपयोग नहीं कर सकते हैं, आप हर्को जैसी मुफ्त सेवा का उपयोग कर सकते हैंएक Node.js WebSocket सर्वर स्थापित करने के लिए और अपने सर्वर से उस पर क्रॉस-डोमेन अनुरोध करने के लिए। बस यह सुनिश्चित करें कि यदि आप ऐसा करते हैं तो अपने WebSocket सर्वर को सेट करने के लिए क्रॉस-ऑरिजिनल अनुरोधों को संभालने में सक्षम होना चाहिए।


Thx, मैं इसे अपने तरीके से करने की कोशिश करूँगा। आप इस PHP सर्वर के प्रदर्शन को कैसे दर करते हैं?
धर्मन

@ डरमन, यह कहना मुश्किल है क्योंकि मैं केवल इसे अपने लोकलहोस्ट पर चला पा रहा हूं। बेशक यह वहाँ ठीक काम करता है, लेकिन एक भारी लोड के साथ एक वास्तविक सर्वर पर, मुझे नहीं पता। मुझे लगता है कि यह काफी अच्छी तरह से चलेगा, क्योंकि मेरे कोड में कोई ब्लोट नहीं है।
हार्टलेसन

1
फिलहाल मेरे पास यह नहीं है, लेकिन निकट भविष्य में, मैं सभी कोड के साथ पूरी प्रक्रिया के बारे में एक ट्यूटोरियल लिखने की योजना बना रहा हूं। एक बार ऐसा करने के बाद, मैं लिंक पोस्ट करूंगा।
हार्टलेसन

1
@ हर्टलेसन: नमस्कार! मुझे आपके कोड को देखने में बहुत दिलचस्पी होगी। क्या आप इसे ऑनलाइन डाल सकते हैं, या मुझे व्यक्तिगत रूप से भेज सकते हैं?
अमर

हां, यह जल्द ही ऑनलाइन होगा। इसके लिए जो मांगा है, सभी को क्षमा करें। मैं अभी हाल ही में इतना व्यस्त रहा हूं। मैं जल्द ही इस पर पहुंचूंगा।
हार्टलेसन

26

जहाँ तक मुझे पता है कि शाफ़्ट इस समय उपलब्ध सबसे अच्छा PHP वेबस्केट समाधान है। और चूंकि यह खुला स्रोत है आप देख सकते हैं कि लेखक ने PHP का उपयोग करके इस वेबसकेट समाधान का निर्माण कैसे किया है।


2
मैं यहां अपना समाधान जोड़ता हूं जिसमें
रैचेट और सिलेक्स

8

सॉकेट का उपयोग क्यों न करें http://uk1.php.net/manual/en/book.sockets.php ? यह अच्छी तरह से प्रलेखित है (न केवल PHP के संदर्भ में) और इसके अच्छे उदाहरण हैं http://uk1.php.net/manual/en/sockets.examples.php


2
हां, आपके पास सादे PHP सॉकेट और एक वेब पेज के बीच एक निरंतर संबंध हो सकता है, मैंने इसे कई बार परीक्षण किया है।
विमेंस जूल

@WiMantis: नमस्कार! क्या आप एक कोड उदाहरण रख सकते हैं जो यह ऑनलाइन करता है, या वैकल्पिक रूप से इसे व्यक्तिगत रूप से मुझे भेज सकता है?
अमर

क्या आप इसका नियमित HTTP कनेक्शन के साथ उपयोग कर सकते हैं? मैं एक DDD ढांचे का निर्माण कर रहा था और मैं इसके ऊपर एक रैपर क्लास की तरह बनाना चाहता हूं और सॉकेट की कार्यक्षमता प्रदान करना चाहता हूं, मुख्य रूप से वेनिला php में कोर एक्सटेंशन का उपयोग करके

2

मैं थोड़ी देर के लिए आपके जूते में था और अंत में नोड.जेएस का उपयोग करके समाप्त हो गया, क्योंकि यह एक में वेब और सॉकेट सर्वर जैसे हाइब्रिड समाधान कर सकता है। इसलिए php बैकएंड http से नोड वेब सर्वर के लिए अनुरोध सबमिट कर सकता है और फिर इसे वेबसोकेट के साथ प्रसारित कर सकता है। जाने के लिए बहुत प्रभावशाली तरीका।


इसलिए हमें php से नोड सर्वर तक http अनुरोध करने के लिए http क्लाइंट का उपयोग करना होगा?
किरेन शिव

किरेन शिव, सही है। कर्ल या समान, फिर नोड वेबसोकेट के माध्यम से एक संदेश प्रसारित करता है
MZ

1

Base64_encoding से पहले कुंजी को हेक्स से बदलने की आवश्यकता है और फिर इसे हैंडशेक के लिए भेजें।

$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";

मुझे बताएं क्या इससे मदद मिलती है।


-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);
    }
}

एक टर्मिनल रन से: php server.php

दूसरे टर्मिनल रन से: इको "हेलो वोरल्ड" | एनसी 127.0.0.1 8002


1
इसका क्या मतलब है?
धर्मन

8
यह सॉकेट है, न कि वेबसॉकेट। एक बड़ा अंतर है।
क्रिस

@techexpander, सवाल के अनुसार आपको पूर्व के बजाय खरोंच से समझाना होगा। जो काम नहीं कर रहा है।
Jaymin
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.