На прошлой неделе я сообщил о важной вехе в нашем проекте музыкального визуализатора: успешном создании (из статических файлов) HD-видео (со звуком) с использованием Node.js и FFMpeg.

Наша конечная цель: генерировать тридцать обработанных клиентом кадров WebGL в секунду, передавать их через сокет в микросервис Node.js, создавать видео и загружать его на YouTube. Звучит безумно? Вы держите пари. Но это безумный, безумный мир, и это может случиться.

Когда я в последний раз остановился, в дополнение к нашему браузерному клиенту (построенному с помощью Three.js, React и PureMVC) у нас было доказательство концепции на стороне сервера, работающее на Node.js, FFMpeg и PureMVC. Этот POC просто запустился, создал команду FFMpeg, настроил ее с расположением наших изображений и аудио, а затем выдал видео, которое можно было без проблем загрузить вручную на YouTube.

Время добавить сокеты в микс

Давайте просто уберем это прямо сейчас: веб-сокеты — это здорово, но есть оооочень много библиотек, конкурирующих за ваше внимание. Исследование становится настоящим поглотителем времени. Хотя теперь у нас есть поддержка Websocket во всех основных браузерах (кроме Opera Mini), если вы не хотите иметь дело с низкоуровневыми деталями, такими как обработка повторных подключений, вы, вероятно, просто захотите выбрать библиотеку. И, пожалуйста, сделайте всем одолжение: не раздражайтесь, решите написать свой собственный и бросьте его в микс. Если вы чувствуете эти побуждения, я умоляю вас сделать форк чужого, сделать его лучше и отправить запрос на включение.

Мои исследования привели меня к выбору Socket.io, хотя:

  1. Его документация ужасна.
  2. Две его демонстрационные версии на месте не работают.
  3. Ссылка на его Slack-канал не работает.
  4. У него есть функции, выходящие за рамки основного бизнеса управления сокетами (например, «комнаты»), которые добавляют к текущему списку ошибок и запросов функций, что затрудняет определение того, действительно ли работают все необходимые вам функции. Прибейте прицел, чуваки, пожалуйста.

Несмотря на все это, вышеупомянутое связанное исследование и огромное количество загрузок заставили меня поверить, что это может быть правильным выбором для нашего проекта. Хотя бы для начала. Детали его реализации достаточно схожи со всеми остальными вариантами, так что даже если его потом нужно будет заменить, рефакторинг не будет таким уж плохим.

Шаг 1. Добавьте Socket.io в проект Node.

npm install socket.io --save

Шаг 2: Создайте сокет-сервер

// Create the socket server
const PORT = 3000;
var socket = require('socket.io')(PORT);

Шаг 3: подключение из командной строки

Как только приведенный выше код был сложен в мой POC, у меня был работающий сервер сокетов, и я хотел пообщаться с ним. Telnet подключился, но никуда меня не привел, потому что, хотя я мог подключиться, протокол рукопожатия основан на заголовке. Все, что я пытался отправить, немедленно закрывало сокет.

Итак, я нашел и установил wscat, который должен был позволить мне подключиться и отправить сообщение с помощью:

wscat -c ws://localhost:3000 -m Allo?

Но без игральных костей.

Я засек время попытки подключиться через командную строку, опустив сообщение, пробуя разные варианты, изучая протокол и т. д., в конце концов сдавшись и перейдя к более сложному, но необходимому этапу создания тестового HTML-клиента.

Тем не менее, я вернулся к этой проблеме во время написания этой статьи, потому что не мог смириться с тем, что не существует простого способа подключения к серверу Socket.io через командную строку. Оказывается, причина, по которой wscat не работал, в том, что Socket.io не говорит по протоколу Websocket. Это было бы неплохой записью для документации Socket.io или страницы часто задаваемых вопросов, но, увы, насколько я могу судить, она нигде на сайте не упоминается.

Ответ — iocat, который взаимодействует либо с Websockets, либо с Socket.io. Установите его следующим образом:

npm install iocat -g

И поговорите с ним так:

$ iocat --socketio ws://localhost:3000
> Allo?

Это хорошо, но, конечно, я уже перешел к созданию тестовой HTML-страницы. Я просто хотел, чтобы вы знали об этом клиенте командной строки.

Также стоит упомянуть Monitor.io, который предоставляет хороший telnet-интерфейс для мониторинга и отладки приложений Socket.io. Это пригодится при тестировании нескольких одновременных подключений, особенно с огромным объемом данных, которые мы планируем передавать через каждое.

Шаг 4: Отправьте сообщение из браузера

Существует удобный ресурс на веб-сайте websocket.org, который позволяет вам подключаться к серверу сокетов, используя встроенную в браузер поддержку веб-сокетов (с безопасным уровнем веб-сокетов или без него), проверять эхо-сообщение и отключаться. Также на этой странице есть фрагмент HTML, который вы можете использовать для создания собственного теста.

Взяв за основу этот код, я провел рефакторинг, чтобы использовать Socket.io вместо Websocket:

<!-- test-io-client.html -->
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
<script>
function init()
    {
        output = document.getElementById("output");
        testSocket();
    }
function testSocket()
    {
        var socket = io.connect('http://localhost:3000/');
        socket.on('test', onMessage );
        socket.on('connect', onConnect );
        socket.on('disconnect', onDisconnect );
        socket.on('connect_error', onError );
        socket.on('reconnect_error', onError );
function onConnect(evt)
        {
            writeToScreen("CONNECTED");
            doSend("Allo?");
        }
function onDisconnect(evt)
        {
            writeToScreen("DISCONNECTED");
        }
function onMessage(data)
        {
            writeToScreen('<span style="color: blue;">RESPONSE: ' + data+'</span>');
            socket.close();
        }
function onError(message)
        {
            writeToScreen('<span style="color: red;">ERROR:</span> ' + message);
        }
function doSend(message)
        {
            writeToScreen("SENT: " + message);
            socket.emit('test', message);
        }
function writeToScreen(message)
        {
            var pre = document.createElement("p");
            pre.style.wordWrap = "break-word";
            pre.innerHTML = message;
            output.appendChild(pre);
        }
    }
window.addEventListener("load", init, false);
</script>
<h2>Socket.io Test</h2>
<div id="output"></div>

Приведенный выше код на стороне клиента подключится к серверу сокетов и выдаст событие «тест» с сообщением. Он также будет прослушивать «тестовое» событие с сервера, отображать его содержимое, а затем отключаться. И он показывает сообщения о подключении и отключении, а также несколько возможных ошибок.

Шаг 5. Обработка входящих подключений на сервере

В серверном проекте я добавил быстрый автономный аналог тестовому клиенту, который будет прослушивать «тестовые» события, регистрировать их в консоли и отвечать клиенту, создавая собственное «тестовое» событие с приветственным сообщением, которое включает идентификатор подключения клиента.

// test-io-server.js
// Create the socket server
const PORT = 3000;
var socket = require('socket.io')(PORT);
socket.on('connection', function(client) {
// Listen for test and disconnect events
    client.on('test', onTest);
    client.on('disconnect', onDisconnect);
// Handle a test event from the client
    function onTest(data) {
        console.log('Received: "' + data + '" from client: ' + client.id);
        client.emit('test', "Cheers, " + client.id);
    }
// Handle a disconnection from the client
    function onDisconnect() {
        console.log('Received Disconnect from client: ' + client.id);
        client.removeListener('test', onTest);
        client.removeListener('disconnect', onDisconnect);
    }
});
Now, from Webstorm (or some inferior IDE):
  • Запустите файл test-io-server.js, чтобы запустить его в Node, и начните прослушивать порт 3000.
  • Запустите файл test-io-client.html, чтобы запустить сервер и передать клиент в браузер.

Это было достаточно легко. Вы можете взять код из этого Gist на Github.

Следующие шаги

Теперь мне нужно:

  • Проталкивание изображений через сокет (возможно, начиная с Socket.io версии 1.0)
  • Храните изображения во временной папке,
  • Запустите сборку видео (в которой я уже разобрался пару статей назад),
  • Почистите исходники.

На данный момент разумно встроить эти аспекты в микрослужбу, а не создавать еще одну одноразовую тестовую установку. Я расскажу об этом в следующей статье и немного углублюсь в архитектуру.

Примечание автора.Эта статья является частью серии, в которой мой партнер и я тестируем разработку нашего продукта "на открытом воздухе".

Это диаметрально противоположно моему типичному подходу «работы скунса», заключающемуся в том, чтобы месяцами сидеть взаперти в бессмысленных попытках сохранить в секрете то, что в конечном итоге все равно станет достоянием общественности. Мы не строим ничего ошеломляющего, изменяющего парадигму или строящего империю. Просто что-то классное, что служит нише, которую мы знаем и заинтересованы в помощи. Это визуализатор 3D-музыки, созданный на HTML5/WebGL с использованием Three.js, PureMVC, React и Node.js. Когда мы закончим, вы сможете создать классное видео для своей аудиодорожки и загрузить его на YouTube.

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

The previous article in this series is: Creating Video on the Server in Node.js
The next article is: WebGL Performance Challenge: Render Thirty Frames per Second and Send them to the Server
This article has been reblogged at the following sites:
DZone: http://bit.ly/node-js-and-socket-io