Чат на Node.js

Чат на Node.js

Основы Node и работу с модулями я описывал в предыдущий статьях. Этого вполне достаточно чтобы написать чат, используя Node.js в связке с Socket.IO. Socket.IO это готовая библиотека для работы с websokets.

Для примера был взят сервер под ОС Ubuntu, соттветсвенно все примеры будут под нее.

Установка компонентов

Итак, предполагается что Node.js и NPM у вас уже стоит. Если нет, как установить node смотрим тут, а npm устанавливаем так:


apt-get install npm

Далее, соответсвенно ставим socket.io:


npm install socket.io

Серверная часть

Структура серверной части следующая: сервер принимает сообщение, если это команда — выполняет определенные действия, если просто сообщение — рассылает всем остальным участникам.


// Подключаем модуль и ставим на прослушивание 5502-порта - 80й обычно занят под http-сервер
var io = require('socket.io').listen(5502); 

// Навешиваем обработчик на подключение нового клиента
io.sockets.on('connection', function (socket) {
	// Т.к. чат простой - в качестве ников пока используем первые 5 символов от ID сокета
	var ID = (socket.id).toString().substr(0, 5);
	var time = (new Date).toLocaleTimeString();
	// Посылаем клиенту сообщение о том, что он успешно подключился и его имя
	socket.json.send({'event': 'connected', 'name': ID, 'time': time});
	// Посылаем всем остальным пользователям, что подключился новый клиент и его имя
	socket.broadcast.json.send({'event': 'userJoined', 'name': ID, 'time': time});
	// Навешиваем обработчик на входящее сообщение
	socket.on('message', function (msg) {
		var time = (new Date).toLocaleTimeString();
		// Уведомляем клиента, что его сообщение успешно дошло до сервера
		socket.json.send({'event': 'messageSent', 'name': ID, 'text': msg, 'time': time});
		// Отсылаем сообщение остальным участникам чата
		socket.broadcast.json.send({'event': 'messageReceived', 'name': ID, 'text': msg, 'time': time})
	});
	// При отключении клиента - уведомляем остальных
	socket.on('disconnect', function() {
		var time = (new Date).toLocaleTimeString();
		io.sockets.json.send({'event': 'userSplit', 'name': ID, 'time': time});
	});
});

В данном коде (и немного больше):

io.sockets — выбор всех подключенных клиентов

io.sockets.sockets[ID] — выбор конкретно взятого клиента с id ID

socket — выбор «текущего» клиента

socket.send(TEXT) — «базовое» событие, отправка сообщения TEXT

socket.json.send({}) — отправка сообщения в формате JSON

socket.broadcast.send — отправка сообщения всем клиентам, кроме текущего

socket.emit(EVENT, JSON) — отправка пользовательского события EVENT с данными JSON

socket.on(EVENT, CALLBACK) — вызов функции CALLBACK при возникновении события EVENT

Я передаю клиентам сообщения в JSON, т.к. сам текст сообщения автоматически генерируется в клиенте. Таким образом представление данных не зависит от сервера и его легко изменить, не прикасаясь к нему (например, изменить язык); к тому же передается меньший обьем данных между сервером и клиентом.

Клиентская часть

HTML и CSS

index.html:


<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<link href="style.css" rel="stylesheet">
		<script src="http://188.225.38.167:5502/socket.io/socket.io.js"></script>
		<script src="script.js"></script>
	</head>
	<body>
		<div id="log"></div><br>
		<input type="text" id="input" autofocus><input type="submit" id="send" value="Send">
	</body>
</html>

style.css


#log {
	width: 590px;
	height: 290px;
	border: 1px solid rgb(192, 192, 192);
	padding: 5px;
	margin-bottom: 5px;
	font: 11pt 'Palatino Linotype';
	overflow: auto;
}
#input {
	width: 550px;
}
#send {
	width: 50px;
}

.in {
	color: rgb(0, 0, 0);
}
.out {
	color: rgb(0, 0, 0);
}
.time {
	color: rgb(144, 144, 144);
	font: 10pt 'Courier New';
}
.system {
	color: rgb(165, 42, 42);
}
.user {
	color: rgb(25, 25, 112);
}

Javascript

socket.io.js автоматически отдается Node.js по адресу nodeJsIp[:port]/socket.io/socket.io.js — ничего дополнительно делать не надо.

script.js


// Создаем текст сообщений для событий
strings = {
	'connected': '[sys][time]%time%[/time]: Вы успешно соединились к сервером как [user]%name%[/user].[/sys]',
	'userJoined': '[sys][time]%time%[/time]: Пользователь [user]%name%[/user] присоединился к чату.[/sys]',
	'messageSent': '[out][time]%time%[/time]: [user]%name%[/user]: %text%[/out]',
	'messageReceived': '[in][time]%time%[/time]: [user]%name%[/user]: %text%[/in]',
	'userSplit': '[sys][time]%time%[/time]: Пользователь [user]%name%[/user] покинул чат.[/sys]'
};
window.onload = function() {
	// Создаем соединение с сервером; websockets почему-то в Хроме не работают, используем xhr
	if (navigator.userAgent.toLowerCase().indexOf('chrome') != -1) {
		socket = io.connect('http://46.182.31.65:8080', {'transports': ['xhr-polling']});
	} else {
		socket = io.connect('http://46.182.31.65:8080');
	}
	socket.on('connect', function () {
		socket.on('message', function (msg) {
			// Добавляем в лог сообщение, заменив время, имя и текст на полученные
			document.querySelector('#log').innerHTML += strings[msg.event].replace(/\[([a-z]+)\]/g, '<span class="$1">').replace(/\[\/[a-z]+\]/g, '</span>').replace(/\%time\%/, msg.time).replace(/\%name\%/, msg.name).replace(/\%text\%/, unescape(msg.text).replace('<', '<').replace('>', '>')) + '<br>';
			// Прокручиваем лог в конец
			document.querySelector('#log').scrollTop = document.querySelector('#log').scrollHeight;
		});
		// При нажатии Enter или кнопки отправляем текст
		document.querySelector('#input').onkeypress = function(e) {
			if (e.which == '13') {
				// Отправляем содержимое input'а, закодированное в escape-последовательность
				socket.send(escape(document.querySelector('#input').value));
				// Очищаем input
				document.querySelector('#input').value = '';
			}
		};
		document.querySelector('#send').onclick = function() {
			socket.send(escape(document.querySelector('#input').value));
			document.querySelector('#input').value = '';
		};		
	});
};

Наконец, запускаем сервер с логом в файл и в фоновом режиме —

node server.js > output.log &

Код протестирован и работает в Opera 11+, Firefox 5+, Chrome 12+. IE9 судя по логам соединяется, получает и отправляет пакеты, но на браузере это не отражается.

Поделитесь статьей со своими друзьями