Вы до сих пор используете long-polling для постоянного взаимодействия клиента и сервера? Тогда мы идём к вам!
В этой статье вы узнаете, как создать беспрерывное соединение клиента и сервера. Уникальность системы в том, что сервер может отправлять данные по своей инициативе, без запроса клиентом.
Расширение веб-сокетов установлено в большинстве хостингов с поддержкой PHP. Для начала давайте создадим скрипт и проверим, доступны ли они:
Если вы получили сообщение о поддержке веб-сокетов, можете продолжать читать дальше, всё отлично. В противном случае придётся установить php_sockets.dll расширение. Не буду вас учить, как его устанавливать, поскольку более или менее опытный кодер умеет это делать. Примечание: Denwer часто ругается на это расширение. Рекомендуем в таком случае качать его с официального сайта php
Вернёмся к нашим сокетам. Для начала давайте различим эти понятия - сокет и веб-сокет. Сокеты известны людям из древних времён - каждая приличная многопользовательская игра использовала именно такую технологию. Постепенно она перекочевала и в веб. Сейчас технология лишь на стадии развития, но у неё большие перспективы. Несмотря на это, не все спешат отказывать flash/ajax.
При разработке игры рекомендуется всё же задуматься над этим: постоянное взаимодействие клиента и сервера, сервер на консольном php, отсутствие необходимости в куче http запросов. Заманчиво? Приступим тогда к написанию самого скрипта. Создайте его и назовите как угодно (например, sockets.php). Кроме этого, подготовьте файлы socket.html и socket.js. Откройте новый html-документ и напишите там макет:
В данном примере мы будем использовать форму для контроля соединения, чтобы вы могли самостоятельно ощупать все аспекты работы с сервером. В форме по умолчанию будет стоять адрес ws://echo.websocket.org. Это - официальный сокет-сервер, служащий для проверки работы браузера с веб-сокетами. Его суть проста - он просто принимает запрос и отдаёт такой же ответ (эхо-сервер).
Однако это лишь макет и форма. Сам контроль клиента будет находиться в файле socket.js, который вы создавали ранее. Давайте откроем его и введём следующее:
Код
"use strict";
(function () { // переменная, которая хранит соединение var socket;
//////////////////////////////////////////////////////////////////////////// var init = function () { // функция инициализации соединения
socket = new WebSocket(document.getElementById("sock-addr").value); // получаем ip и порт с формы
socket.onopen = connectionOpen; // назначанием обработчик соединения socket.onmessage = messageReceived; // назначаем обработчик получения сообщения от сервера //socket.onerror = errorOccurred; //socket.onopen = connectionClosed;
document.getElementById("sock-send-butt").onclick = function () { // при нажатии кнопки отправить... socket.send(document.getElementById("sock-msg").value); // отправляем сообщение активному серверу };
document.getElementById("sock-disc-butt").onclick = function () { // когда нажал на кнопку отсоединиться... connectionClose(); // отсоединился, заметка от кэпа };
document.getElementById("sock-recon-butt").onclick = function () { // при кнопке соединиться... socket = new WebSocket(document.getElementById("sock-addr").value); // соединяемся по IP и порту в форме socket.onopen = connectionOpen; socket.onmessage = messageReceived; };
};
function connectionOpen() { socket.send("Connection with \""+document.getElementById("sock-addr").value+"\" Подключение установлено обоюдно, отлично!"); // при открытии отправляем соединение серверу }
function messageReceived(e) { console.log("Ответ сервера: " + e.data); document.getElementById("sock-info").innerHTML += (e.data+"<br />"); // добавляем ответ в консоль }
function connectionClose() { socket.close(); document.getElementById("sock-info").innerHTML += "Соединение закрыто <br />"; // добавляем сообщение о закрытии в консоль
Поскольку код прокомментирован, нет нужны объяснять принцип работы полностью. Загрузилась страница - устанавливаем соединение, и мы готовы к приёму-отдаче сообщений. Видите, как просто? Не надо устанавливать множество соединений, как в случае с AJAX (setInterval, $.ajax).
Итак, мы написали нормальный клиент. Давайте откроем socket.html в интернет-браузере. Если вы всё сделали правильно, соединение немедленно установится и вы сможете отправлять сообщения. Ответы будут полностью аналогичны запросам. Отлично, мы создали хороший клиент. Давайте возьмёмся за самое сложное - сервер.
Что такое сервер? Это обычный php-скрипт, но запускается он из-под консоли ОС, а не из браузера. Нужно, чтобы он был запущен, и не закрывался. Для этого установим set_time_limit(0) и ignore_user_abort(true) (это всё пишется прямо в скрипте).
Для запуска скрипта в UNIX системах надо использовать такую команду:
Код
$ php -q scriptfile.php &
Знак & в конце обязателен, иначе при выходе из терминала скрипт перестанет работать. В Windows системах также можно запускать php-скрипты, однако необходимо знать, где размещён php.exe:
Дальше идёт самая главная часть скрипта - мы должны настроить приём и обработку соединений. Для удобства в главном цикле при получении сообщения и прочих событиях вызывается отдельная функция. То есть мы разделим сам приём соединений и обработчик. Код понять несложно:
if (!stream_select($read, $write, $except, null)) {//ожидаем сокеты доступные для чтения (без таймаута) break; }
if (in_array($socket, $read)) {//есть новое соединение то обязательно делаем handshake //принимаем новое соединение и производим рукопожатие: if (($connect = stream_socket_accept($socket, -1)) && $info = handshake($connect)) { echo "new connection...<br />"; echo "connect=".$connect.", info=".$info."<br />OK<br />"; //echo "info<br />"; //var_dump($info);
$connects[] = $connect;//добавляем его в список необходимых для обработки onOpen($connect, $info);//вызываем пользовательский сценарий } unset($read[ array_search($socket, $read) ]); }
foreach($read as $connect) {//обрабатываем все соединения $data = fread($connect, 100000);
if (!$data) { //соединение было закрыто echo "connection closed...<br />"; fclose($connect); unset($connects[ array_search($connect, $connects) ]); onClose($connect);//вызываем пользовательский сценарий continue; }
Данный цикл будет выполняться до тех пор, пока жив сокет-сервер. В данном примере его жизнь неограничена. Для теста мы развернули обычный эхо-сервер и проверяли его каждый день. Упал он лишь через 5 дней, и то дело было в рестарте сервера.
Когда цикл перестал выполняться, нужно "собрать мусор", закрыв соединение.
Код
fclose($socket);
Как оказалось, сокет-сервер в php и каждое соединение - переменная типа Ресурс, поэтому с ним можно работать, как с обычным ресурсом файла - записывать, читать и т.п.
В коде выше мы настроили вызов пользовательских сценариев. Пока мы их не написали, поэтому толку от такого сервера нет. Во-первых, давайте напишем функцию "рукопожатия" - она будет вызвана для самого первого обмена байтами между клиентом и сервером. Другими словами, при соединении клиент и сервер познакомятся. И соединение будет записано. Код прокомментирован, разобраться просто:
Код
function handshake($connect) { //Функция рукопожатия $info = array();
Любое сообщение сервера или клиента должно быть зашифровано, поскольку прямой текст передавать нельзя. Это логично, ведь не каждый символ может нормально быть разобран, поэтому используется base64. Алгоритм видим ниже:
/** * 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; }
Более-менее хороший веб-сокет сервер готов. Он будет действовать как официальный эхо-сервер - просто отвечать аналогично запросу.
Как я уже говорил раньше, хорошего должно быть понемногу, поэтому на сегодня это всё. В следующем уроке мы сделаем простой, как дверь, анонимный чат с использованием веб-сокетов. Ничего сложного!
Удачи вам и всего хорошего. Если нашли недочёты или замечания - пишите в комментарии. Предупреждаю сразу - это были веб-сокеты с нуля. Я знаю, что есть Daemon.io и прочие готовые сервера, но чем хуже собственная разработка?
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:
Игровые объявления и предложения:
Если вас заинтересовал материал «MMORPG на PHP: веб-сокеты», и вы бы хотели прочесть что-то на эту же тему, то вы можете воспользоваться списком схожих материалов ниже. Данный список сформирован автоматически по тематическим меткам раздела.
Предлагаются такие схожие материалы:
Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.