Приветствую вас, уважаемые читатели. В предыдущей статье я рассказал, как сделать простую звонилку в браузере при помощи PeerJS. А сегодня планирую рассмотреть, как обмениваться сообщениями между двумя пользователями напрямую без задержек.
Кому это интересно? Если Вы разрабатываете онлайн игру, в которой необходим быстрый обмен данными между игроками, тогда прямой обмен сообщениями это пожалуй то, что вам нужно.
Я покажу, как работает технология, на примере простого чата между двумя пользователями, а также расскажу как адаптировать код для обмена игровыми данными.
Начнем с первичной разметки и инициализации объекта peer
В заголовке (head) мы подключаем PeerJS. Какую роль играют элементы с индексами myid и otherPeerId смотрите в статье о звонке
Массив messList будет хранить ленту сообщений. Функция addMess будет добавлять элементы в этот массив и выводить его содержимое в контейнер переписки.
Далее идет инициализация объекта peer, которая также описана в прошлой статье.
Теперь немного о соединениях. Чтобы установить соединение необходимо, чтоб один участник, зная peerID другого, начал соединение с ним, а второй — получил это соединение.
Событие 'connection' для объекта peer происходит при входящем соединении. А функция connect объекта peer устанавливает такое соединение. В обоих случаях будем сохранять объект соединение в переменную conn. Поскольку дальнейшие действия с соединением для текущего учебного примера будут идентичны (хотя в боевом проекте разница может присутствовать), я вынес в отдельную функцию initConn.
Здесь вешаем 2 обработчика: на открытие и на закрытие соединения. В обработчике на открытие соединения довешываем о��работчик на прием данных, который будет добавлять в контейнер диалога прилетевшее сообщение.
Остается только реализовать функцию, которая будет отправлять сообщение по нажатию кнопки Отправить, которая:
Что необходимо сделать, чтоб посылать таким же методом не обычный текст, а данные, которыми нужно обмениваться в процессе игр? На самом деле ничего особенного. В JS есть методы JSON.stringify и JSON.parse которые преобразуют объект в строку и обратно. Просто заверните ваши данные объект, преобразуйте объект в строку (JSON.stringify) перед отправкой и превратите полученные данные в объект (JSON.parse) при получении
Обычно для пересылки игровых объектов и текстовых сообщений не нужны большие объемы данных. Но если вы собираетесь переслать содержимое целого контейнера на странице (куча HTML кода) имейте в виду, что большое соединение может не дойти в неизменном виде.
Из личного опыта скажу: не стоит пересылать таким способом сообщения больше 10 КБ (~10 000 символов). Лучше такое сообщение записать во временный файл и послать партнеру команду на чтение кода из этого файла (думаю смысл вы у��овили).
На этом можно было бы остановиться, если бы не…
Да, такое происходит. Виной тому бывает нестабильный интернет. Бывало ли так, что вы уже почти выиграли, но обрывается соединение и вы теряете весь свой прогресс? Чтобы такого избежать, давайте допишем код, который будет поднимать упавшее соединение. Будем для этого обрабатывать событие 'close'. Это событие возникает если:
Здесь мы с задержкой в 2 секунды после обрыва соединения просто пытаемся установить новое.
partnerPeer у объекта conn присутствует только у установившего в первый раз соединение партнера, а значит только одна из 2-х сторон соединения начнет его восстанавливать при обрыве.
И теперь весь код целиком:
Кому это интересно? Если Вы разрабатываете онлайн игру, в которой необходим быстрый обмен данными между игроками, тогда прямой обмен сообщениями это пожалуй то, что вам нужно.
Разметка и инициализация
Я покажу, как работает технология, на примере простого чата между двумя пользователями, а также расскажу как адаптировать код для обмена игровыми данными.
Начнем с первичной разметки и инициализации объекта peer
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>PeerJS Обмен сообщениями</title>
<script src="https://unpkg.com/peerjs@1.0.0/dist/peerjs.min.js"></script>
</head>
<body>
<h3>Мой ID: <span id=myid ></span></h3>
<input id=otherPeerId type=text placeholder="otherPeerId" ><button onclick="connectToNode(document.getElementById('otherPeerId').value)">Соединиться</button>
<div id=messages style="width:400px;height:60vh; background:#ADD8E6;margin:5px;">
</div><br>
<textarea id=mess style="width:400px;height:15vh" ></textarea><br>
<button onclick="sendMess(document.getElementById('mess'))">Отправить</button>
<script>
var messList=[];
function addMess(mess) {
messList.push(mess);
document.getElementById('messages').innerHTML=messList.join("");
}
var peer=new Peer(); //инициализация peer
var conn; //переменная, хранящая соединение
peer.on('open', function(peerID) {
document.getElementById('myid').innerHTML=peerID;
});
</script>
</body>
В заголовке (head) мы подключаем PeerJS. Какую роль играют элементы с индексами myid и otherPeerId смотрите в статье о звонке
Массив messList будет хранить ленту сообщений. Функция addMess будет добавлять элементы в этот массив и выводить его содержимое в контейнер переписки.
Далее идет инициализация объекта peer, которая также описана в прошлой статье.
Теперь немного о соединениях. Чтобы установить соединение необходимо, чтоб один участник, зная peerID другого, начал соединение с ним, а второй — получил это соединение.
Установка соединения
peer.on('connection', function(c) { //входящее соединение...
conn=c;
initConn();
});
function connectToNode(partnerPeer) { //исходящее соединение...
conn = peer.connect(partnerPeer);
conn.partnerPeer=partnerPeer;
initConn();
}
Событие 'connection' для объекта peer происходит при входящем соединении. А функция connect объекта peer устанавливает такое соединение. В обоих случаях будем сохранять объект соединение в переменную conn. Поскольку дальнейшие действия с соединением для текущего учебного примера будут идентичны (хотя в боевом проекте разница может присутствовать), я вынес в отдельную функцию initConn.
function initConn() {
conn.on ('open', function () { //открыто соединение
addMess("<div><h4>Соединение установлено</h4></div>");
conn.on ('data', function (data) { //прилетело сообщение
addMess("<div><b>Партнер: </b>"+data+"</div>");
});
});
conn.on('close',function() {addMess('-----------Соединение разорвано-------------');});
}
Здесь вешаем 2 обработчика: на открытие и на закрытие соединения. В обработчике на открытие соединения довешываем о��работчик на прием данных, который будет добавлять в контейнер диалога прилетевшее сообщение.
Остается только реализовать функцию, которая будет отправлять сообщение по нажатию кнопки Отправить, которая:
- добавляет сообщение в свою ленту
- отправляет сообщение партнеру (метод send у объекта соединение)
- очищает поле ввода сообщения
function sendMess(elem) { addMess("<div><b>Я: </b>"+elem.value+"</div>"); conn.send(elem.value); elem.value=""; }
Адаптация к пересылке игровых данных
Что необходимо сделать, чтоб посылать таким же методом не обычный текст, а данные, которыми нужно обмениваться в процессе игр? На самом деле ничего особенного. В JS есть методы JSON.stringify и JSON.parse которые преобразуют объект в строку и обратно. Просто заверните ваши данные объект, преобразуйте объект в строку (JSON.stringify) перед отправкой и превратите полученные данные в объект (JSON.parse) при получении
//отправка
gameObject={x:2,y:5,...}
conn.send(JSON.stringify(gameObject));
//получение
conn.on ('data', function (data) { //прилетело сообщение
gameObject=JSON.parse(data);
});
Обычно для пересылки игровых объектов и текстовых сообщений не нужны большие объемы данных. Но если вы собираетесь переслать содержимое целого контейнера на странице (куча HTML кода) имейте в виду, что большое соединение может не дойти в неизменном виде.
Из личного опыта скажу: не стоит пересылать таким способом сообщения больше 10 КБ (~10 000 символов). Лучше такое сообщение записать во временный файл и послать партнеру команду на чтение кода из этого файла (думаю смысл вы у��овили).
На этом можно было бы остановиться, если бы не…
Обрыв соединения
Да, такое происходит. Виной тому бывает нестабильный интернет. Бывало ли так, что вы уже почти выиграли, но обрывается соединение и вы теряете весь свой прогресс? Чтобы такого избежать, давайте допишем код, который будет поднимать упавшее соединение. Будем для этого обрабатывать событие 'close'. Это событие возникает если:
- соединение было закрыто намеренно
- соединение пропало из-за плохого интернета или партнер попросту закрыл вкладку
conn.on('close',function() { setTimeout(function() { if(conn.partnerPeer) { var pp=conn.partnerPeer; conn = peer.connect(conn.partnerPeer); conn.partnerPeer=pp; initConn(); } else conn=null; } ,2000); addMess('-----------Соединение разорвано-------------'); });
Здесь мы с задержкой в 2 секунды после обрыва соединения просто пытаемся установить новое.
partnerPeer у объекта conn присутствует только у установившего в первый раз соединение партнера, а значит только одна из 2-х сторон соединения начнет его восстанавливать при обрыве.
И теперь весь код целиком:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>PeerJS Обмен сообщениями</title>
<script src="https://unpkg.com/peerjs@1.0.0/dist/peerjs.min.js"></script>
</head>
<body>
<h3>Мой ID: <span id=myid ></span></h3>
<input id=otherPeerId type=text placeholder="otherPeerId" ><button onclick="connectToNode(document.getElementById('otherPeerId').value)">Соединиться</button>
<div id=messages style="width:400px;height:60vh; background:#ADD8E6;margin:5px;">
</div><br>
<textarea id=mess style="width:400px;height:15vh" ></textarea><br>
<button onclick="sendMess(document.getElementById('mess'))">Отправить</button>
<script>
var messList=[];
function addMess(mess) {
messList.push(mess);
document.getElementById('messages').innerHTML=messList.join("");
}
var peer=new Peer();
var conn; //переменная, хранящая соединение
peer.on('open', function(peerID) {
document.getElementById('myid').innerHTML=peerID;
});
peer.on('connection', function(c) { //входящее соединение...
conn=c;
initConn();
});
function connectToNode(partnerPeer) { //исходящее соединение...
conn = peer.connect(partnerPeer);
initConn();
}
function initConn() {
conn.on ('open', function () { //открыто соединение
addMess("<div><h4>Соединение установлено</h4></div>");
conn.on ('data', function (data) { //прилетело сообщение
addMess("<div><b>Партнер: </b>"+data+"</div>");
});
});
conn.on('close',function() {addMess('-----------Соединение разорвано-------------');});
}
function sendMess(elem) {
addMess("<div><b>Я: </b>"+elem.value+"</div>");
conn.send(elem.value);
elem.value="";
}
</script>
</body>