Chat em tempo real com NodeJs e Android - Parte 1

Todos com certa experiência em programação já deve ter ouvido falar de Socket (de rede), e já pensou em usar esta tecnologia na web? Sim, é possível através do WebSocket.

Obs: Todo o projeto foi feito utilizando o sistema operacional Windows.

WebSocket

O WebSocket é um meio de comunicação bidirecional, ou seja, ambos os lados, tanto cliente quanto servidor enviam e recebem dados ao mesmo tempo pelo mesmo canal. Tem um protocolo próprio baseado no TCP e utiliza o protocolo HTTP apenas para a comunicação inicial.

Socket.io

Existe uma biblioteca chamada Socket.io escrita em Javascript que permite o uso de WebSockets nos lado do cliente pelos browsers que suportam HTML5. Também é possível usá-la no lado do servidor utilizando o Node.js.

Para este texto, irei usar a verão 1.3.7 da Socket.io para o Node.js (lado do servidor) e para o browser (lado do cliente), a versão 0.6.2 para o Android (lado do cliente também).

Node.js

A versão do Node.js usada neste texto é a 5.1.0.

Criado por Ryan Dahl, o Node.js (ou somente Node para os mais íntimos) é um servidor web que interpreta código Javascript através da engine V8, criada pela Google e a mesma utilizada no navegador Google Chrome.

O objetivo do Node é a criação de aplicações web altamente escaláveis. Também foi feito para aguentar milhares de requisições simuntâneas numa mesma máquina, top ein?

Mão na massa

Servidor - Preparando

Vamos começar com a base do projeto: o servidor. Primeiro vá para a página oficial do Node.js e faça o download da última versão, que no meu caso foi a 5.1.0.

Aviso: Tenha certeza de que irá instalar o npm junto (por padrão já vem marcado para instalar, mas não custa garantir).

Para inciarmos um projeto, abra o prompt, vá até a pasta desejada e digite:

npm init

Serão feitas algumas perguntas (responda a todas elas) e será criado o package.json que nada mais é o equivalente ao csproj dos projetos C# no Visual Studio.

Agora vamos instalar dois módulos do Node: o Express.js e o Socket.io. O Express.js é um framework para o Node.js, com ele iremos facilitar nosso trabalho. Não que seja muito neste exemplo, mas é um ótimo framework.

Para o Express.js, digite:

npm install express --save

E para o Socket.io, digite:

npm install socket.io --save

Apenas aguarde os dois comandos serem finalizados. Ao final, você terá instalado o Express.js e o Socket.io no seu projeto, salvando os fontes deles junto ao projeto.

Servidor - Estruturando

Crie um arquivo "index.js" e vamos começar:

var app = require('express')();
var http = require('http').createServer(app);

Por enquanto, estamos criando uma instância do Express e uma do servidor.

Para iniciar o servidor, precisamos colocar o comando abaixo.

http.listen(81, function() {
console.log('Servidor funcionando!');
});

Estamos fazendo o servidor escutar na porta 81 e imprimir "Servidor funcionando!" no console em caso de sucesso.

Vamos testar! No prompt e ainda na pasta do projeto, digite:

node index.js

Pronto, já temos o servidor rodando. Ele não faz nada além de escutar a porta 81, então vamos criar uma página principal.

Servidor - Página inicial

Antes do comando http.listen, vamos definir a página principal:

var app = require('express')();
var http = require('http').createServer(app);

app.get('/', function(req, res) {
res.sendFile(__dirname + '\\index.html');
});

http.listen(81, function() {
console.log('Servidor funcionando!');
});

Com este código, estamos definido que ao receber uma requisição GET na URL "/", enviaremos o arquivo "index.html" para o navegador.

Note os dois parâmetros req e res, o primeiro é o objeto da requição, onde podemos acessar todas as informações da requisição que desejarmos, e o segundo é o objeto de resposta, e é através dele que devolvemos alguma informação ao requisitante, seja o navegador ou não.

Também existe a variável "__dirname", que é uma variável do Node onde está armazenada o diretório atual do servidor.

Para a página principal, teremos somente a estrura:

<html>
<head>
<title>Socket.IO Chat</title>
</head>
<body>
<h1>Socket.IO Chat</h1>

<input type="text" id="mensagem"/>
<br>
<input type="button" id="btn_enviar" value="Enviar"/>

<br>
<br>

<ul id="mensagens">
</ul>
</body>
</html>

Salve o arquivo como "index.html", execute o servidor e acesse "localhost:81" no seu navegador para ver o resultado.

Servidor - Recebendo conexões de sockets

Agora vamos colocar o Socket no servidor. Então voltemos ao "index.js" e onde foram declaradas as variáveis "app" e "http", crie a variável "io".

var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io').listen(http);

Precisamos configurar a conexão de um novo socket e como o Socket.io trabalha com eventos, usaremos o evento "connect".

io.sockets.on('connection', function(socket) {
console.log('conectado: ' + socket.id);
});

O callback passado será executado cada vez que um novo socket se conectar, portanto este é o momento de configurarmos ele. Vamos saber quem se desconectou pelo evento "disconnect":

io.sockets.on('connection', function(socket) {
console.log('conectado: ' + socket.id);

socket.on('disconnect', function() {
console.log('desconectado: ' + socket.id);
});
});

Note que o evento desta vez foi vinculado diretamente no socket, portanto estamos dizendo: quando este socket se desconectar, execute isto.

Página principal - Conectando via socket

De volta ao "index.html", dentro da tag HEAD, vamos declarar o Javascript do Socket.io:

<script src="https://cdn.socket.io/socket.io-1.3.7.js"></script>

Agora vamos criar uma nova instância do socket (ainda na tag HEAD):

<script type="text/javascript">
window.onload = function () {
var socket = io();
};
</script>

Desta maneira, o socket já irá tentar conexão com o servidor, e sempre na a rota "/". Caso deseje mudar esta convenção, existe um recurso chamado namespace, é interessante dar uma olhada na documentação sobre isso.

Página principal - Enviando dados

Vamos preparar para enviar uma mensagem ao servidor.

window.onload = function () {
var socket = io();

document.getElementById('btn_enviar').onclick = function () {
var inputMensagem = document.getElementById('mensagem');

socket.emit('mensagem_servidor', inputMensagem.value);

inputMensagem.value = '';
};
};

Neste caso, definimos o evento onclick do botão "btn_enviar". Ao clicarmos no botão, obtemos o texto digitado e o enviamos pelo socket através do comando socket.emit. Note que também vinculamos a um nome de evento, no nosso caso o "mensagem_servidor".

Página principal - Recebendo dados

Para recebermos informações do servidor, precisamos usar o comando socket.on, onde dizemos: no evento X, faça isto.

window.onload = function () {
var socket = io();

socket.on('mensagem_cliente', function(dados) {
var li = document.createElement('li');
li.appendChild(document.createTextNode(dados));

document.getElementById('mensagens').appendChild(li);
});

document.getElementById('btn_enviar').onclick = function () {
var inputMensagem = document.getElementById('mensagem');

socket.emit('mensagem_servidor', inputMensagem.value);

inputMensagem.value = '';
};
};

Fizemos a recepção dos dados através do evento "mensagem_cliente" no comando socket.on e adicionamos à lista.

Servidor - Recebendo dados

Para receber os dados no servidor, usamos a mesma idéia do socket.on:

io.sockets.on('connection', function(socket) {
console.log('conectado: ' + socket.id);

socket.on('mensagem_servidor', function(data) {
console.log('recebido: ' + data);
});

socket.on('disconnect', function() {
console.log('desconectado: ' + socket.id);
});
});

Bacana não?

Servidor - Enviando dados

Como estamos fazendo um chat, cada mensagem recebida precisa ser enviada para o resto dos usuários na sala, ou seja, iremos enviar os dados para todos os sockets conectados.

Neste exemplo, iremos reenviar a mensagem recebida para o socket para que a mensagem da pessoa apareca para ela mesma. Entendo que podemos evitar isso fazendo isso direto no Javascript que é executado no navegador, mas vamos ver como funciona a coisa.

Para enviar qualquer informação do servidor para um cliente (socket conectado), usamos o comando socket.emit. E para enviar para todos os clientes conectados, usamos o socket.broadcast.emit. Veja:

io.sockets.on('connection', function(socket) {
console.log('conectado: ' + socket.id);

socket.on('mensagem_servidor', function(data) {
console.log('recebido: ' + data);

socket.emit('mensagem_cliente', data);
socket.broadcast.emit('mensagem_cliente', data);
});

socket.on('disconnect', function() {
console.log('desconectado: ' + socket.id);
});
});

Assim cada mensagem recebida é reenviada ao cliente e também enviada aos outros clientes. Note que ao usar o socket.broadcast.emit, o socket em questão não recebe a informação, para isto, é necessário o uso do socket.emit.

Vamos melhorar mais um pouco e exibir quem se conectou e quem desconectou:

io.sockets.on('connection', function(socket) {
console.log('conectado: ' + socket.id);

socket.emit('mensagem_cliente', socket.id + ' conectado');
socket.broadcast.emit('mensagem_cliente', socket.id + ' conectado');

socket.on('mensagem_servidor', function(data) {
console.log('recebido: ' + data);

socket.emit('mensagem_cliente', data);
socket.broadcast.emit('mensagem_cliente', data);
});

socket.on('disconnect', function() {
console.log('desconectado: ' + socket.id);

socket.broadcast.emit('mensagem_cliente', socket.id + ' desconectado');
});
});

Pronto, já podemos executar o Node. Apenas digite no console (na mesma pasta do projeto):

node index.js

Acessando a página através da url http://localhost:81/ e ver a mágica acontecer.

O projeto está no Github, e se desejar vê-lo por completo, clique aqui (no repositório já tem a implementação do Android, mas irei explicar no próximo post).

Até mais.