Страницы

Поиск по вопросам

вторник, 25 февраля 2020 г.

PHP-реализация WebSockets

#php #websocket #html5


Здравствуйте, написал небольшой скрипт для работы с WebSockets относительно скриптов
в паблике.
По идее должно работать отлично, но всё не так, как хотелось бы :(
Когда я отправляю данные из браузера (подопытный Google Chrome 18) на сервер, то
php-демон должен писать мне эти данные в терминал. Однако, вместо данных присылаются
непонятные рандомные символы. Расскажите пожалуйста, каким образом их нужно дешифровать
или же исправьте мой код, пожалуйста. 
Спасибо заранее.
client.html
server.php    


Ответы

Ответ 1



Вы неправильно ознакомились с протоколом WebSocket. Если говорить конкретно по вашему вопросу, то вы не учитываете факт того, что данные фрейма содержат не только переданные вами данные, но еще дополнительную информацию. Например данные практически всеми браузерами маскируются (т.е. шифруются) с помощью маски. Формат фрейма выглядит примерно так: В любом случае вы неправильно реализовали протокол и я рекомендую почитать вам описание websocket на javascript.ru.

Ответ 2



попробуй вместо $buffer = substr($buffer,strpos($buffer,"Sec-WebSocket-Key: ")+19); написать mb_substr($buffer,strpos($buffer,"Sec-WebSocket-Key: ")+19,'utf-8'); не посмотрел внимательно на код но это должно помочь при такой ситуациии

Ответ 3



Для отправки текста необходимо его декодить по правилам, указанным выше. Вот готовое решение: function encode($payload, $type = 'text', $masked = true) { $frameHead = array(); $frame = ''; $payloadLength = strlen($payload); switch ($type) { case 'text': // first byte indicates FIN, Text-Frame (10000001): $frameHead[0] = 129; break; case 'close': // first byte indicates FIN, Close Frame(10001000): $frameHead[0] = 136; break; case 'ping': // first byte indicates FIN, Ping frame (10001001): $frameHead[0] = 137; break; case 'pong': // first byte indicates FIN, Pong frame (10001010): $frameHead[0] = 138; break; } // set mask and payload length (using 1, 3 or 9 bytes) if ($payloadLength > 65535) { $payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8); $frameHead[1] = ($masked === true) ? 255 : 127; for ($i = 0; $i < 8; $i++) { $frameHead[$i + 2] = bindec($payloadLengthBin[$i]); } // most significant bit MUST be 0 (close connection if frame too big) if ($frameHead[2] > 127) { $this->close(1004); return false; } } elseif ($payloadLength > 125) { $payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8); $frameHead[1] = ($masked === true) ? 254 : 126; $frameHead[2] = bindec($payloadLengthBin[0]); $frameHead[3] = bindec($payloadLengthBin[1]); } else { $frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength; } // convert frame-head to string: foreach (array_keys($frameHead) as $i) { $frameHead[$i] = chr($frameHead[$i]); } if ($masked === true) { // generate a random mask: $mask = array(); for ($i = 0; $i < 4; $i++) { $mask[$i] = chr(rand(0, 255)); } $frameHead = array_merge($frameHead, $mask); } $frame = implode('', $frameHead); // append payload to frame: for ($i = 0; $i < $payloadLength; $i++) { $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i]; } return $frame; } function decode($data) { $unmaskedPayload = ''; $decodedData = array(); // estimate frame type: $firstByteBinary = sprintf('%08b', ord($data[0])); $secondByteBinary = sprintf('%08b', ord($data[1])); $opcode = bindec(substr($firstByteBinary, 4, 4)); $isMasked = ($secondByteBinary[0] == '1') ? true : false; $payloadLength = ord($data[1]) & 127; // unmasked frame is received: if (!$isMasked) { return array('type' => '', 'payload' => '', 'error' => 'protocol error (1002)'); } switch ($opcode) { // text frame: case 1: $decodedData['type'] = 'text'; break; case 2: $decodedData['type'] = 'binary'; break; // connection close frame: case 8: $decodedData['type'] = 'close'; break; // ping frame: case 9: $decodedData['type'] = 'ping'; break; // pong frame: case 10: $decodedData['type'] = 'pong'; break; default: return array('type' => '', 'payload' => '', 'error' => 'unknown opcode (1003)'); } if ($payloadLength === 126) { $mask = substr($data, 4, 4); $payloadOffset = 8; $dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset; } elseif ($payloadLength === 127) { $mask = substr($data, 10, 4); $payloadOffset = 14; $tmp = ''; for ($i = 0; $i < 8; $i++) { $tmp .= sprintf('%08b', ord($data[$i + 2])); } $dataLength = bindec($tmp) + $payloadOffset; unset($tmp); } else { $mask = substr($data, 2, 4); $payloadOffset = 6; $dataLength = $payloadLength + $payloadOffset; } /** * We have to check for large frames here. socket_recv cuts at 1024 bytes * so if websocket-frame is > 1024 bytes we have to wait until whole * data is transferd. */ if (strlen($data) < $dataLength) { return false; } if ($isMasked) { for ($i = $payloadOffset; $i < $dataLength; $i++) { $j = $i - $payloadOffset; if (isset($data[$i])) { $unmaskedPayload .= $data[$i] ^ $mask[$j % 4]; } } $decodedData['payload'] = $unmaskedPayload; } else { $payloadOffset = $payloadOffset - 4; $decodedData['payload'] = substr($data, $payloadOffset); } return $decodedData; } Для отправки в сокет данных делаем так: fwrite($connect, encode('hello'));. Ну и чтение соответственно.

Комментариев нет:

Отправить комментарий