Отправка сообщений peer-to-peer при помощи PeerJS

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Приветствую вас, уважаемые читатели. В предыдущей статье я рассказал, как сделать простую звонилку в браузере при помощи PeerJS. А сегодня планирую рассмотреть, как обмениваться сообщениями между двумя пользователями напрямую без задержек.
Кому это интересно? Если Вы разрабатываете онлайн игру, в которой необходим быстрый обмен данными между игроками, тогда прямой обмен сообщениями это пожалуй то, что вам нужно.

Разметка и инициализация


Я покажу, как работает технология, на примере простого чата между двумя пользователями, а также расскажу как адаптировать код для обмена игровыми данными.
Начнем с первичной разметки и инициализации объекта 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 обработчика: на открытие и на закрытие соединения. В обработчике на открытие соединения довешываем обработчик на прием данных, который будет добавлять в контейнер диалога прилетевшее сообщение.
Остается только реализовать функцию, которая будет отправлять сообщение по нажатию кнопки Отправить, которая:
1) добавляет сообщение в свою ленту
2) отправляет сообщение партнеру (метод send у объекта соединение)
3) очищает поле ввода сообщения
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'. Это событие возникает если:
1) соединение было закрыто намеренно
2) соединение пропало из-за плохого интернета или партнер попросту закрыл вкладку

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>    
Источник: https://habr.com/ru/post/470189/


Интересные статьи

Интересные статьи

В этой статье мы рассмотрим, как система управления 1С-Битрикс справляется с большими нагрузками. Данный вопрос особенно актуален сегодня, когда электронная торговля начинает конкурировать по обороту ...
Возможность интеграции с «1С» — это ключевое преимущество «1С-Битрикс» для всех, кто профессионально занимается продажами в интернете, особенно для масштабных интернет-магазинов.
Компании переполнили рынок товаров и услуг предложениями. Разнообразие наблюдается не только в офлайне, но и в интернете. Достаточно вбить в поисковик любой запрос, чтобы получить подтверждение насыще...
В Челябинске проходят митапы системных администраторов Sysadminka, и на последнем из них я делал доклад о нашем решении для работы приложений на 1С-Битрикс в Kubernetes. Битрикс, Kubernetes, Сep...
На сегодняшний день у сервиса «Битрикс24» нет сотен гигабит трафика, нет огромного парка серверов (хотя и существующих, конечно, немало). Но для многих клиентов он является основным инструментом ...