Настройка WebSocket на сайте для быстрого обмена данными
Доброго времени друзья! Вкратце решил описать как организовать обмен данными клиент-сервер, на базе протокола WebSocket, в рамках сайта, например для мгновенного обмена сообщениями в чате. Принцип работы следующий — запускаем скрипт на PHP, работающий в фоновом режиме(это сервер WS), вешаем его на порт(например на 8090), далее настраиваем проксирование Apache или nGinx, чтобы все запросы из вне, по протоколу WS(с HTTP заголовком Upgrate: websocket) переправлялись на localhost:8090. Затем подключаемся к серверу из JavaScript, слушаем и обрабатываем данные. Отправляем сообщения в сокет из PHP. Собственно всё. Далее подробно.
Скрипт на PHP
Пример скрипта на PHP, который выступает в роли сервера WebSocket, сам скрипт я разбирать не буду, там достаточно все просто(работает на основе PHP расширения Socket), но если что-то не понятно, будет повод разобраться.
<?php $sServer = new socketServer(); $sServer->host = 'localhost'; $sServer->patch = 'server.php'; $sServer->port = 8090; $sServer->init(); class socketServer{ public $host; public $patch; public $port; private $socket; private $clients; function __construct(){ $this->socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); } public function init(){ socket_set_option( $this->socket, SOL_SOCKET, SO_REUSEADDR, 1 ); socket_bind( $this->socket, 0, $this->port ); socket_listen( $this->socket ); $this->clients = [ $this->socket ]; while( true ){ $null = []; $nSockets = $this->clients; socket_select( $nSockets, $null, $null, 0, 10 ); if( in_array( $this->socket, $nSockets ) ){ $nSocket = socket_accept( $this->socket ); $headers = socket_read( $nSocket, 1024 ); $this->clients[] = $nSocket; $this->headers( $headers, $nSocket ); unset( $nSockets[ array_search( $this->socket, $nSockets ) ] ); } foreach( $nSockets as $nsData ){ while( @ socket_recv( $nsData, $sData, 1024, 0 ) >= 1 ){ $socketMessage = $this->decode( $sData ); if( $args = json_decode( $socketMessage, true ) ) $this->send( $args, $this->clients ); break 2; } $sData = @ socket_read( $nsData, 1024, PHP_NORMAL_READ ); if( $sData === false ){ unset( $this->clients[ array_search( $nsData, $this->clients ) ] ); } } } } private function headers( $headersStr, $socket ){ $headers = []; $headersStr = preg_split("/\r\n/", $headersStr); foreach( $headersStr as $hStr ){ $hStr = rtrim( $hStr ); if( preg_match('/\A(\S+): (.*)\z/', $hStr, $match ) ) $headers[ $match[1] ] = $match[2]; } $key = base64_encode( pack('H*', sha1( $headers['Sec-WebSocket-Key'] .'258EAFA5-E914-47DA-95CA-C5AB0DC85B11' ) ) ); $header = "HTTP/1.1 101 Switching Protocols \r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "WebSocket-Origin: $this->host" . "WebSocket-Location: ws://$this->host:$this->port$this->patch\r\n" . "Sec-WebSocket-Accept: $key\r\n\r\n"; socket_write( $socket, $header, strlen( $header ) ); } private function send( $args ){ $args = array_merge( $args, [ 'time' => date('H:i') ] ); $msg = $this->encode( json_encode( $args ) ); $length = strlen( $msg ); foreach( $this->clients as $client ) @socket_write( $client, $msg, $length ); return true; } private function encode( $sData ){ $b = 0x81; $header = ''; $length = strlen( $sData ); if( $length <= 125 ) $header = pack( 'CC', $b, $length ); elseif( $length > 125 && $length < 65536 ) $header = pack( 'CCn', $b, 126, $length ); elseif( $length > 65536 ) $header = pack( 'CCNN', $b, 127, $length ); return $header . $sData; } private function decode( $sData ){ $str = ''; $length = ord( $sData[1] ) & 127; if( $length == 126 ){ $mask = substr( $sData, 4, 4 ); $data = substr( $sData, 8 ); } elseif( $length == 127 ){ $mask = substr( $sData, 10, 4 ); $data = substr( $sData, 14 ); } else { $mask = substr( $sData, 2, 4 ); $data = substr( $sData, 6 ); } for( $i=0; $i < strlen( $data ); $i++ ) $str .= $data[ $i ] ^ $mask[ $i%4 ]; return $str; } function __destruct(){ socket_close( $this->socket ); } }?> |
Запускаем демон на PHP (сервер WS)
Скрипт запускается в режиме демона, командой:
php server.php & |
Настраиваем проксирование Apache на сервер WS
Чтобы все запросы, пришедшие на наш сайт, такого вида https://example.com/socket/ по протоколу WS/WSS, перенаправлялись на WebSocket сервер, что слушает порт 8090, в настройка Apache, добавляем следующее:
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /socket/ ws://localhost:8090 [P,L] |
Если у вас на сервере стоит связка nGing и Apache, то проксирование можно настроить только на nGinx.
Отправляем сообщение в WebSocket из PHP
Отправляем сообщения на WebSocket, из PHP(используя Stream) по протоколу HTTPS:
<php $args = []; // Массив с отправляемыми данными $port = 443; $host = 'example.com'; $headers = "GET /socket/ HTTP/1.1\r\n" . "Host: $host\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Key: " . base64_encode( random_bytes( 16 ) ) . "\r\n" . "Sec-WebSocket-Version: 13\r\n\r\n"; $context = stream_context_create( [ 'ssl' => [ 'verify_peer' => false, 'verify_peer_name' => false ] ] ); $socket = stream_socket_client("ssl://$host:$port", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context); if( $socket ){ fwrite( $socket, $headers ); $response = fread( $socket, 1024); fwrite( $socket, $this->wsEncode( json_encode( $args ) ) ); fclose( $socket ); } |
Подключаем скрипт на JavaScript к WS
Прослушиваем WebSocket на стороне сайта из JavaScript:
var socket = new WebSocket('wss://example.com/socket/'), app.socket.onopen = () => {}; app.socket.onclose = () => {}; app.socket.onerror = error => {}; app.socket.onmessage = function(event) { var data = JSON.parse( event.data ); // Обработка пришедших сообщений }; |
Друзья, если вам нужна можешь в настройке WebSocket на вашем сайте, обращайтесь, буду раз сотрудничеству!

Автор: 






