{"id":227815,"date":"2014-06-26T23:57:03","date_gmt":"2014-06-26T19:57:03","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=227815"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=227815","title":{"rendered":"<span class=\"post_title\">PhP-WebSocket Server<\/span>"},"content":{"rendered":"<div class=\"content html_format\">     \t\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442!<\/p>\n<p>  \u041d\u0435 \u0442\u0430\u043a \u0434\u0430\u0432\u043d\u043e \u044f \u043d\u0430\u0447\u0430\u043b \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442, \u0441\u0432\u043e\u0435\u0433\u043e \u0440\u043e\u0434\u0430 \u0447\u0430\u0442, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0434\u043e \u043c\u043e\u0435\u0433\u043e \u0432\u043c\u0435\u0448\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u043a\u043e\u043c\u0435\u0442-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u044b. \u0420\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 WebSockets \u044f \u043d\u0435 \u0441\u0442\u0430\u043d\u0443, \u0442.\u043a \u044f \u0434\u0443\u043c\u0430\u044e, \u0447\u0442\u043e \u044d\u0442\u043e \u0432\u0441\u0435\u043c \u0443\u0436\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e, \u0434\u0430 \u0438 \u043f\u043e\u0441\u0442 \u043d\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0434 \u043a\u0430\u0442\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e PhP-\u043a\u043e\u0434\u0430. <br \/>  <a name=\"habracut\"><\/a><\/p>\n<p>  \u0414\u0430, \u044f \u0437\u043d\u0430\u044e, \u0447\u0442\u043e \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u043f\u043e\u043b\u043d\u044b\u043c-\u043f\u043e\u043b\u043d\u043e, \u043e\u0434\u043d\u0430\u043a\u043e \u043c\u0435\u043d\u044f \u043e\u043d\u0438 \u043f\u043e \u0442\u0435\u043c \u0438\u043b\u0438 \u0438\u043d\u044b\u043c \u043f\u0440\u0438\u0447\u0438\u043d\u0430\u043c \u043d\u0435 \u0443\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u0438 \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 PhP WebSocket Server. \u041f\u043b\u044e\u0441 \u043a\u043e \u0432\u0441\u0435\u043c\u0443, \u044d\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \ud83d\ude42 \u0422\u0430\u043a \u043a\u0430\u043a \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u044f \u043d\u0435 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043b\u0441\u044f \u0441 \u0441\u043e\u043a\u0435\u0442\u0430\u043c\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c \u043d\u0430 \u0434\u0435\u043b\u0435, \u0447\u0442\u043e \u044d\u0442\u043e \u0437\u0430 \u0431\u043b\u044e\u0434\u043e \u0438 \u0441 \u0447\u0435\u043c \u0435\u0433\u043e \u0435\u0434\u044f\u0442, \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u043e\u043a\u0443\u0440\u0438\u0442\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043c\u0430\u043d\u0443\u0430\u043b\u043e\u0432. \u0422\u0430\u043a \u0436\u0435 \u044f \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b \u0438 \u0438\u0437\u0443\u0447\u0438\u043b \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043b\u043e \u043c\u043d\u0435 \u043b\u0443\u0447\u0448\u0435 \u0443\u0437\u043d\u0430\u0442\u044c \u0438\u0445. \u0421\u0430\u043c \u043a\u043e\u0434 \u044f \u0432\u044b\u043b\u043e\u0436\u0443 \u0432 \u043a\u043e\u043d\u0446\u0435 \u0441\u0442\u0430\u0442\u044c\u0438, \u0430 \u043f\u0435\u0440\u0435\u0434 \u044d\u0442\u0438\u043c \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u0430\u0440\u0443 \u0441\u043b\u043e\u0432 \u043e\u0431 \u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438. <\/p>\n<p>  \u0421\u0440\u0430\u0437\u0443 \u0441\u043a\u0430\u0436\u0443, \u0447\u0442\u043e \u044d\u0442\u043e \u043f\u0435\u0440\u0432\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430 (\u0442\u0430\u043a \u0441\u043a\u0430\u0437\u0430\u0442\u044c 1.0) \u0438 \u0435\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0435\u0449\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u044b\u0440\u043e\u0432\u0430\u0442. <\/p>\n<p>  \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u0449\u0435\u0435:<\/p>\n<pre><code class=\"php\">\/\/ \u0418\u043d\u043a\u043b\u0443\u0434\u0438\u043c \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 require_once &quot;.\/WebSocketApi.php&quot;; \/\/.. $wsApi = new WebSocketApi(); \/\/ \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c ! $wsApi-&gt;startWServer(&quot;127.0.0.7&quot;, 8000); <\/code><\/pre>\n<p>  \u041d\u0438\u0447\u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e, \u043d\u0435 \u043f\u0440\u0430\u0432\u0434\u0430 \u043b\u0438?<br \/>  \u0412 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0435\u0441\u0442\u044c 3 event&#8217;a. <\/p>\n<ul>\n<li>onOpen \u2014 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 <\/li>\n<li>onMsg \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430<\/li>\n<li>onClose \u2014 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c <\/li>\n<\/ul>\n<p>  \u042d\u0442\u0438 event&#8217;\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043d\u0430\u043c \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445. <\/p>\n<p>  \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"php\">\/\/ \u0418\u043d\u043a\u043b\u0443\u0434\u0438\u043c \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 require_once &quot;.\/WebSocketApi.php&quot;; \/\/.. $wsApi = new WebSocketApi(); \/\/ \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0448\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0438 \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c $wsApi-&gt;events['onOpen'] = array(     &quot;myFunction&quot;, &quot;myFunction_2&quot; ); \/\/ \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c ! $wsApi-&gt;startWServer(&quot;127.0.0.7&quot;, 8000); <\/code><\/pre>\n<p>  \u0418\u043b\u0438 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"php\">\/\/ \u0418\u043d\u043a\u043b\u0443\u0434\u0438\u043c \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 require_once &quot;.\/WebSocketApi.php&quot;; \/\/.. $wsApi = new WebSocketApi(); \/\/ \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0448\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0438 \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c $wsApi-&gt;events['onMsg'] = array(     &quot;function_1&quot; =&gt; array(&quot;param_1&quot;, &quot;param_2&quot;),     &quot;function_2&quot; =&gt; array(&quot;param_1&quot;) ); \/\/ \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c ! $wsApi-&gt;startWServer(&quot;127.0.0.7&quot;, 8000); <\/code><\/pre>\n<p>  \u0410 \u0432\u043e\u0442 \u0438 \u0441\u0430\u043c \u043a\u043e\u0434: <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0422\u044b\u043d\u0446<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"php\">&lt;?php \/**  * User: byabuzyak  * Date: 6\/23\/14  * Time: 9:12 PM  *\/ class WebSocketApi {      const MAX_FRAME_RECV            = 100000;     const MAX_TIMEOUT               = 25;      const TIMEOUT_PONG              = 5;     const TIMEOUT_RECV              = 10;      const OPCODE_CONTINUATION       = 0;     const OPCODE_TEXT               = 1;     const OPCODE_BINARY             = 2;     const OPCODE_CLOSE              = 8;     const OPCODE_PING               = 9;     const OPCODE_PONG               = 10;      const READY_STATE_CONNECTING    = 0;     const READY_STATE_OPEN          = 1;     const READY_STATE_CLOSING       = 2;     const READY_STATE_CLOSED        = 3;      const PAYLOAD_LENGTH_16         = 126;     const PAYLOAD_LENGTH_63         = 127;      const STATUS_PROTOCOL_ERROR     = 1002;     const STATUS_MESSAGE_TOO_BIG    = 1004;      const FIN                       = 128;     const MASK                      = 128;       \/**      * @var array      *\/     public $clients                 = array();     \/**      * @var array      *\/     public $ws                      = array();      \/**      * @var array      *\/     public $events                  = array();     \/**      * @var int      *\/     public $clientsCount            = 0;      \/**      * @var null      *\/     public static $_instance        = null;      \/**      * @return WebSocketApi|null      *\/     public static function getInstance(){         if(self::$_instance === null){             self::$_instance = new self();         }          return self::$_instance;     }      \/**      * @param string $host      * @param int $port      * @return bool      *\/     public function startWServer($host = '127.0.0.1', $port = 8000){         if(isset($this-&gt;ws[0]))             return false;          if(phpversion() &gt;= 5.5){             cli_set_process_title(&quot;WebSockets Server 1.0&quot;);         }          if (!$this-&gt;ws[0] = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) {             return false;         }         if (!socket_set_option($this-&gt;ws[0], SOL_SOCKET, SO_REUSEADDR, 1)) {             socket_close($this-&gt;ws[0]);             return false;         }         if (!socket_bind($this-&gt;ws[0], $host, $port)) {             socket_close($this-&gt;ws[0]);             return false;         }         if (!socket_listen($this-&gt;ws[0], 10)) {             socket_close($this-&gt;ws[0]);             return false;         }          $write  = null;         $except = null;          $nextPingCheck = time() + 1;         while(isset($this-&gt;ws[0])){             $read   = $this-&gt;ws;             $count  = socket_select($read, $write, $except, 1);             if($count === false){                 socket_close($this-&gt;ws[0]);                 return false;             }elseif($count &gt; 0){                 foreach($read as $clientId =&gt; $socket){                     if($clientId != 0){                         $buffer = '';                         $bytes  = @socket_recv($socket, $buffer, 4096, 0);                          if($bytes === false){                             $this-&gt;_closeClient($clientId);                         }elseif($bytes &gt; 0){                             if(!$this-&gt;_checkClient($clientId, $buffer, $bytes)){                                 $this-&gt;_closeClient($clientId);                             }                         }else{                             $this-&gt;_removeClient($clientId);                         }                     }else{                         $client = socket_accept($this-&gt;ws[0]);                         if($client !== false){ \/\/                            TODO: \u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u043c\u0430\u043a\u0441. \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432                             $ip = '';                             $result = socket_getpeername($client, $ip);                             $ip     = ip2long($ip);                             if($result !== false){                                 $this-&gt;_addClient($client, $ip);                             }else{                                 socket_close($client);                             }                         }                     }                 }             }             if(time() &gt;= $nextPingCheck){                 $nextPingCheck = time() + 1;                 $this-&gt;_checkActiveClients();             }         }         return true;     }      \/**      *      *\/     private function _checkActiveClients(){         $time = time();         foreach($this-&gt;clients as $clientId =&gt; $socket){             if($socket-&gt;state != self::READY_STATE_CLOSED){                 if($socket-&gt;ready_state !== false){                     if($time &gt;= $socket-&gt;ready_state + self::TIMEOUT_PONG){                         $this-&gt;_closeClient($clientId);                         $this-&gt;_removeClient($clientId);                     }                 }elseif($time &gt;= $socket-&gt;time + self::TIMEOUT_RECV){                     if($socket-&gt;state != self::READY_STATE_CONNECTING){                         $this-&gt;clients[$clientId]-&gt;ready_state = time();                         $this-&gt;sendClientMsg($clientId, self::OPCODE_PING, '');                     }else{                         $this-&gt;_removeClient($clientId);                     }                 }             }         }     }      \/**      * @param $socket      * @param string $clientIp      *\/     private function _addClient($socket, $clientIp = ''){         $this-&gt;clientsCount += 1;         $clientId = $this-&gt;_nextId();          $this-&gt;clients[$clientId]   = (object) array(             'socket'        =&gt; $socket,             'msg_buffer'    =&gt; '',             'state'         =&gt; self::READY_STATE_CONNECTING,             'time'          =&gt; time(),             'ready_state'   =&gt; false,             'close_status'  =&gt; 0,             'client_ip'     =&gt; $clientIp,             'header_length' =&gt; false,             'buffer_length' =&gt; 0,             'buffer'        =&gt; '',             'msg_opcode'    =&gt; 0,             'msg_data_len'  =&gt; 0         );         $this-&gt;ws[$clientId]        = $socket;     }      \/**      * @return int      *\/     private function _nextId(){         $i = 1;         while(isset($this-&gt;ws[$i]))             $i++;         return $i;     }      \/**      * @param $clientId      *\/     private function _removeClient($clientId){         if(array_key_exists('onClose', $this-&gt;events)){             foreach($this-&gt;events['onClose'] as $function =&gt; $args){                 if(is_array($args)){                     call_user_func_array($function, $args);                 }else{                     call_user_func($function, $args);                 }             }         }          socket_close($this-&gt;clients[$clientId]-&gt;socket);         unset($this-&gt;ws[$clientId], $this-&gt;clients[$clientId]);         $this-&gt;clientsCount -= 1;     }      \/**      * @param $clientId      * @return bool      *\/     private function _closeClient($clientId){         if($this-&gt;clients[$clientId]-&gt;state == self::READY_STATE_CLOSING || $this-&gt;clients[$clientId]-&gt;state == self::READY_STATE_CLOSED){             return true;         }          $this-&gt;clients[$clientId]-&gt;close_status = self::STATUS_PROTOCOL_ERROR;         $this-&gt;sendClientMsg($clientId, self::OPCODE_CLOSE, pack('n', self::STATUS_PROTOCOL_ERROR));         $this-&gt;clients[$clientId]-&gt;state = self::READY_STATE_CLOSING;     }      \/**      * @param $clientId      * @param $opCode      * @param $msg      * @return bool      *\/     public function sendClientMsg($clientId, $opCode, $msg){         if($this-&gt;clients[$clientId]-&gt;state == self::READY_STATE_CLOSING || $this-&gt;clients[$clientId]-&gt;state == self::READY_STATE_CLOSED){             return true;         }          $msgLength  = strlen($msg);         $buffSize   = 4096;          $frameCount = ceil($msgLength \/ $buffSize);         if($frameCount == 0){             $frameCount = 1;         }          $maxFrame = $frameCount - 1;         $lastFrameBuffLength = ($msgLength % $buffSize) != 0 ? $msgLength % $buffSize : ($msgLength != 0 ? $buffSize : 0);          for($i=0; $i&lt;$frameCount; $i++){             $final      = $i != $maxFrame ? 0 : self::FIN;             $opCode     = $i != 0 ? self::OPCODE_CONTINUATION : $opCode;             $buffLength = $i != $maxFrame ? $buffSize : $lastFrameBuffLength;              if($buffLength &lt;= 125){                 $payloadLength      =   $buffLength;                 $payloadLengthExt   =   '';                 $payloadLengthExtL  =   0;             }elseif($buffLength &lt;= 65535){                 $payloadLength      =   self::PAYLOAD_LENGTH_16;                 $payloadLengthExt   =   pack('n', $buffLength);                 $payloadLengthExtL  =   2;             }else{                 $payloadLength      =   self::PAYLOAD_LENGTH_63;                 $payloadLengthExt   =   pack('xxxxN', $buffLength);                 $payloadLengthExtL  =   8;             }              $buffer = pack('n', (($final | $opCode) &lt;&lt; 8) | $payloadLength) . $payloadLengthExt . substr($msg, $i * $buffSize, $buffLength);             $socket = $this-&gt;clients[$clientId]-&gt;socket;             $left   = 2 + $payloadLengthExtL + $buffLength;              do{                 $sent = @socket_send($socket, $buffer, $left, 0);                 if($sent === false){                     return false;                 }                 $left -= $sent;                 if($sent &gt; 0){                     $buffer = substr($buffer, $sent);                 }             }             while($left &gt; 0);         }          return true;     }      \/**      * @param $clientId      * @param $buffer      * @param $bLength      * @return bool      *\/     private function _checkClient($clientId, &$buffer, $bLength){         if($this-&gt;clients[$clientId]-&gt;state == self::READY_STATE_OPEN){             $result = $this-&gt;_buildClientFrame($clientId, $buffer, $bLength);         }elseif($this-&gt;clients[$clientId]-&gt;state == self::READY_STATE_CONNECTING){             $result = $this-&gt;_makeHandShake($clientId, $buffer);             if($result){                 $this-&gt;clients[$clientId]-&gt;state = self::READY_STATE_OPEN;                 if(array_key_exists('onOpen', $this-&gt;events)){                     foreach($this-&gt;events['onOpen'] as $function =&gt; $args){                         if(is_array($args)){                             call_user_func_array($function, $args);                         }else{                             call_user_func($function, $args);                         }                     }                 }             }         }else{             $result = false;         }          return $result;     }      \/**      * @param $clientId      * @param $buffer      * @param $bufferLength      * @return bool      *\/     private function _buildClientFrame($clientId, &$buffer, $bufferLength){         $this-&gt;clients[$clientId]-&gt;buffer_length += $bufferLength;         $this-&gt;clients[$clientId]-&gt;buffer .= $buffer;          if($this-&gt;clients[$clientId]-&gt;header_length !== false || $this-&gt;_checkSizeClientFrame($clientId) == true){             $headerLength   = ($this-&gt;clients[$clientId]-&gt;header_length &lt;= 125 ? 0 : ($this-&gt;clients[$clientId]-&gt;header_length &lt;= 65535 ? 2 : 8)) + 6;             $frameLength    = $this-&gt;clients[$clientId]-&gt;header_length + $headerLength;             if($this-&gt;clients[$clientId]-&gt;buffer_length &gt;= $frameLength){                 $nextFrameLength = $this-&gt;clients[$clientId]-&gt;buffer_length - $frameLength;                 if($nextFrameLength &gt; 0){                     $this-&gt;clients[$clientId]-&gt;buffer_length -= $nextFrameLength;                     $nextFrameBytes         = substr($this-&gt;clients[$clientId]-&gt;buffer, $frameLength);                     $this-&gt;clients[$clientId]-&gt;buffer = substr($this-&gt;clients[$clientId]-&gt;buffer, 0, $frameLength);                 }                  $result = $this-&gt;_processClientFrame($clientId);                  if(isset($this-&gt;clients[$clientId])){                     $this-&gt;clients[$clientId]-&gt;header_length = false;                     $this-&gt;clients[$clientId]-&gt;buffer_length = 0;                     $this-&gt;clients[$clientId]-&gt;buffer = '';                 }                  if($nextFrameLength &lt;= 0 || !$result){                     return $result;                 }                  return $this-&gt;_buildClientFrame($clientId, $nextFrameBytes, $nextFrameLength);             }         }          return true;     }      \/**      * @param $clientId      * @return bool      *\/     private function _processClientFrame($clientId){         $this-&gt;clients[$clientId]-&gt;time = time();          $buffer = &$this-&gt;clients[$clientId]-&gt;buffer;         if(substr($buffer, 5, 1) === false)             return false;          $a1 = ord(substr($buffer, 0, 1));         $a2 = ord(substr($buffer, 1, 1));          $f      = $a1 & self::FIN;         $opCode = $a1 & 15;         $mask   = $a2 & self::MASK;          if(!$mask)             return false;          $seek       = $this-&gt;clients[$clientId]-&gt;header_length &lt;= 125 ? 2 : ($this-&gt;clients[$clientId]-&gt;header_length &lt;= 65535 ? 4 : 10);         $maskKey    = substr($buffer, $seek, 4);          $array = unpack('Na', $maskKey);         $maskKey = $array['a'];         $maskKey = array(             $maskKey &gt;&gt; 24,             ($maskKey &gt;&gt; 16) & 255,             ($maskKey &gt;&gt; 8) & 255,             $maskKey & 255         );         $seek += 4;          if (substr($buffer, $seek, 1) !== false) {             $data = str_split(substr($buffer, $seek));             foreach($data as $key =&gt; $byte){                 $data[$key] = chr(ord($byte) ^ ($maskKey[$key % 4]));             }             $data = implode('', $data);         }else{             $data = '';         }          if ($opCode != self::OPCODE_CONTINUATION && $this-&gt;clients[$clientId]-&gt;msg_data_len &gt; 0) {             $this-&gt;clients[$clientId]-&gt;msg_data_len    = 0;             $this-&gt;clients[$clientId]-&gt;msg_buffer     = '';         }          if($f == self::FIN){             if($opCode != self::OPCODE_CONTINUATION){                 return                     $this-&gt;_processClientMsg($clientId, $opCode, $data, $this-&gt;clients[$clientId]-&gt;header_length);             }else{                 $this-&gt;clients[$clientId]-&gt;msg_data_len += $this-&gt;clients[$clientId]-&gt;header_length;                 $this-&gt;clients[$clientId]-&gt;msg_buffer  .= $data;                  $result = $this-&gt;_processClientMsg($clientId, $this-&gt;clients[$clientId]-&gt;msg_opcode, $this-&gt;clients[$clientId]-&gt;msg_buffer, $this-&gt;clients[$clientId]-&gt;msg_data_len);                  if(isset($this-&gt;clients[$clientId])){                     $this-&gt;clients[$clientId]-&gt;msg_buffer     = '';                     $this-&gt;clients[$clientId]-&gt;msg_opcode    = 0;                     $this-&gt;clients[$clientId]-&gt;msg_data_len    = 0;                 }                  return $result;             }         }else{             if($opCode & 8)                 return false;             $this-&gt;clients[$clientId]-&gt;msg_data_len += $this-&gt;clients[$clientId]-&gt;header_length;             $this-&gt;clients[$clientId]-&gt;msg_buffer  .= $data;              if($opCode != self::OPCODE_CONTINUATION){                 $this-&gt;clients[$clientId]-&gt;msg_opcode = $opCode;             }         }         return true;     }      \/**      * @param $clientId      * @param $opCode      * @param $data      * @param $dataLength      * @return bool      *\/     private function _processClientMsg($clientId, $opCode, &$data, $dataLength){         if($opCode == self::OPCODE_PING){             $this-&gt;sendClientMsg($clientId, self::OPCODE_PONG, $data);         }elseif($opCode == self::OPCODE_PONG){             if($this-&gt;clients[$clientId]-&gt;ready_state !== false){                 $this-&gt;clients[$clientId]-&gt;ready_state = false;             }         }elseif($opCode == self::OPCODE_CLOSE){             if($this-&gt;clients[$clientId]-&gt;state == self::READY_STATE_CLOSING){                 $this-&gt;clients[$clientId]-&gt;state = self::READY_STATE_CLOSED;             }else{ \/\/                TODO: \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0438\u043f\u044b \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f                 $this-&gt;_closeClient($clientId);             }             $this-&gt;_removeClient($clientId);         }elseif($opCode == self::OPCODE_TEXT || $opCode == self::OPCODE_BINARY){             if(array_key_exists('onMsg', $this-&gt;events)){                 foreach($this-&gt;events['onMsg'] as $function =&gt; $args){                     if(is_array($args)){                         call_user_func_array($function, $args);                     }else{                         call_user_func($function, $args);                     }                 }             }         }else{             return false;         }          return true;     }      \/**      * @param $clientId      * @return bool      *\/     private function _checkSizeClientFrame($clientId){         if($this-&gt;clients[$clientId]-&gt;buffer_length &gt; 1){             $payloadLength = ord(substr($this-&gt;clients[$clientId]-&gt;buffer, 1, 1)) & 127;             if ($payloadLength &lt;= 125) {                 $this-&gt;clients[$clientId]-&gt;header_length = $payloadLength;             }elseif($payloadLength == 126){                 if (substr($this-&gt;clients[$clientId]-&gt;buffer, 3, 1) !== false) {                     $payloadLengthExtended  = substr($this-&gt;clients[$clientId]-&gt;buffer, 2, 2);                     $array                  = unpack('na', $payloadLengthExtended);                     $this-&gt;clients[$clientId]-&gt;header_length = $array['a'];                 }             }else{                 if (substr($this-&gt;clients[$clientId]-&gt;buffer, 9, 1) !== false) {                     $payloadLengthExtended      = substr($this-&gt;clients[$clientId]-&gt;buffer, 2, 8);                     $payloadLengthExtended32_1  = substr($payloadLengthExtended, 0, 4);                     $array                      = unpack('Na', $payloadLengthExtended32_1);                      if ($array['a'] != 0 || ord(substr($payloadLengthExtended, 4, 1)) & 128) {                         $this-&gt;_closeClient($clientId, self::STATUS_MESSAGE_TOO_BIG);                         return false;                     }                      $payloadLengthExtended32_2  = substr($payloadLengthExtended, 4, 4);                     $array                      = unpack('Na', $payloadLengthExtended32_2);                      if ($array['a'] &gt; 2147479538) {                         $this-&gt;_closeClient($clientId, self::STATUS_MESSAGE_TOO_BIG);                         return false;                     }                      $this-&gt;clients[$clientId]-&gt;header_length = $array['a'];                 }             }              if ($this-&gt;clients[$clientId]-&gt;header_length !== false) {                 if ($this-&gt;clients[$clientId]-&gt;header_length &gt; self::MAX_FRAME_RECV) {                     $this-&gt;clients[$clientId]-&gt;header_length = false;                     $this-&gt;_closeClient($clientId, self::STATUS_MESSAGE_TOO_BIG);                     return false;                 }                  $controlFrame = (ord(substr($this-&gt;clients[$clientId]-&gt;buffer, 0, 1)) & 8) == 8;                 if (!$controlFrame) {                     $newMessagePayloadLength = $this-&gt;clients[$clientId]-&gt;msg_data_len + $this-&gt;clients[$clientId]-&gt;header_length;                     if ($newMessagePayloadLength &gt; self::MAX_FRAME_RECV || $newMessagePayloadLength &gt; 2147483647) {                         $this-&gt;_closeClient($clientId, self::STATUS_MESSAGE_TOO_BIG);                         return false;                     }                 }                  return true;             }         }         return false;     }      \/**      * @param $clientId      * @param $buffer      * @return bool      *\/     private function _makeHandShake($clientId, $buffer){         $sep = strpos($buffer, &quot;\\r\\n\\r\\n&quot;);         if (!$sep) return false;          $headers = explode(&quot;\\r\\n&quot;, substr($buffer, 0, $sep));         $headersCount = sizeof($headers);         if ($headersCount &lt; 1)             return false;           $request = &$headers[0];         $requestParts = explode(' ', $request);         $requestPartsSize = sizeof($requestParts);         if ($requestPartsSize &lt; 3)             return false;          if (strtoupper($requestParts[0]) != 'GET')             return false;          $httpPart = &$requestParts[$requestPartsSize - 1];         $httpParts = explode('\/', $httpPart);         if (!isset($httpParts[1]) || (float) $httpParts[1] &lt; 1.1)             return false;          $headersKeyed = array();         for ($i=1; $i&lt;$headersCount; $i++) {             $parts = explode(':', $headers[$i]);             if (!isset($parts[1]))                 return false;              $headersKeyed[trim($parts[0])] = trim($parts[1]);         }          if (!isset($headersKeyed['Host']))             return false;          if (!isset($headersKeyed['Sec-WebSocket-Key']))             return false;          $key = $headersKeyed['Sec-WebSocket-Key'];         if (strlen(base64_decode($key)) != 16)             return false;          if (!isset($headersKeyed['Sec-WebSocket-Version']) || (int) $headersKeyed['Sec-WebSocket-Version'] &lt; 7)             return false;          $hash = base64_encode(sha1($key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));          $headers = array(             'HTTP\/1.1 101 Switching Protocols',             'Upgrade: websocket',             'Connection: Upgrade',             'Sec-WebSocket-Accept: '.$hash         );         $headers    = implode(&quot;\\r\\n&quot;, $headers).&quot;\\r\\n\\r\\n&quot;;         $socket     = $this-&gt;clients[$clientId]-&gt;socket;          $left = strlen($headers);         do{             $sent = @socket_send($socket, $headers, $left, 0);             if($sent === false)                 return false;              $left -= $sent;             if($sent &gt; 0)                 $headers = substr($headers, $sent);         }         while($left &gt; 0);          return true;     } }  <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0427\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043d\u0430 \u0433\u0438\u0442\u0445\u0430\u0431\u0435 \u0438 \u0432\u044b\u043b\u043e\u0436\u0443 \u0442\u0443\u0434\u0430. \u0412 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0438 \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0442\u044c. <br \/>  \u0425\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0443\u0441\u043b\u044b\u0448\u0430\u0442\u044c \u043e\u0442 \u0412\u0430\u0441 \u0443\u0432\u0430\u0436\u0430\u0435\u043c\u044b\u0445 \u0441\u043e\u0432\u0435\u0442\u044b, \u043f\u043e\u0436\u0435\u043b\u0430\u043d\u0438\u044f \u0438\u043b\u0438 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u044f. <\/p>\n<p>  \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435! <\/p>\n<p>  P.S \u0432 \u043f\u043b\u0430\u043d\u0430\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u043f\u043b\u044e\u0448\u0435\u043a. \u041f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430 \u043d\u0438\u0445.      \t<\/p>\n<div class=\"clear\"><\/div>\n<\/p><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"http:\/\/habrahabr.ru\/post\/227815\/\"> http:\/\/habrahabr.ru\/post\/227815\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"content html_format\">     \t\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442!<\/p>\n<p>  \u041d\u0435 \u0442\u0430\u043a \u0434\u0430\u0432\u043d\u043e \u044f \u043d\u0430\u0447\u0430\u043b \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442, \u0441\u0432\u043e\u0435\u0433\u043e \u0440\u043e\u0434\u0430 \u0447\u0430\u0442, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0434\u043e \u043c\u043e\u0435\u0433\u043e \u0432\u043c\u0435\u0448\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u043a\u043e\u043c\u0435\u0442-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u044b. \u0420\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 WebSockets \u044f \u043d\u0435 \u0441\u0442\u0430\u043d\u0443, \u0442.\u043a \u044f \u0434\u0443\u043c\u0430\u044e, \u0447\u0442\u043e \u044d\u0442\u043e \u0432\u0441\u0435\u043c \u0443\u0436\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e, \u0434\u0430 \u0438 \u043f\u043e\u0441\u0442 \u043d\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0434 \u043a\u0430\u0442\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e PhP-\u043a\u043e\u0434\u0430.   <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-227815","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/227815","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=227815"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/227815\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=227815"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=227815"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=227815"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}