Aproveite o mês das
carreiras na Alura

Até 44% OFF

Falta pouco!

00

DIAS

00

HORAS

00

MIN

00

SEG

WebSockets HTML5 em Java com Jetty: Web em tempo real

Alura
slopes
slopes

Compartilhe

Navegadores são bons em fazer requisições para o servidor. Mas e o contrário? Fazer o servidor enviar dados pro navegador em momentos arbitrários sempre foi um trabalho. Ajax reverso, comet, long polling são algumas das gambiarrastécnicas usadas. Mas o HTML5 trouxe uma grande novidade: a API de WebSockets.

WebSockets permitem abrir uma conexão com o servidor remoto e trafegar dados arbitrariamente do servidor para o cliente e vice-versa. Dá pra fazer muita coisa com isso. Um chat em tempo real, um mecanismo de sincronização, e até streaming de dados binários.

No último QCon SP 2012, usei WebSockets na minha palestra de Web Mobile para sincronizar os slides de apresentação com os celulares, tablets e notebooks da platéia. Todo mundo abriu uma página com os slides e, conforme a palestra andava, eu mudava o slide no telão e imediatamente a platéia via o novo slide em seus dispositivos, junto com notas e exemplos adicionais.

Os slides foram feitos em HTML, CSS e JS, e o mecanismo de sincronizar meu slide com os mais de 200 dispositivos conectados ao mesmo tempo foi WebSockets. Minha máquina no telão enviava para o servidor qual era o slide atual e este distribuía a informação pra todo mundo em tempo real.

JavaScript no cliente

O código na página é muito simples. Você abre a conexão com o servidor e pode receber ou enviar mensagens. O envio é uma simples chamada de método e o recebimento, um callback JavaScript assíncrono. Há ainda um callback pra você saber quando a conexão for aberta.

No caso do sincronizador de slides, a versão mobile aberta nos dispositivos dos usuários recebia o ID do slide a ser mostrado:

 var ws = new WebSocket('ws://meuservico.com/websockets');
ws.onopen = function() { console.log('Conexão aberta com sucesso'); };
ws.onmessage = function(message) { var slide = document.getElementById(message); mostrarSlide(slide); }; 

Já a máquina principal, que faz a sincronização, envia qual é o slide atual pra todo mundo:

 function mostrarSlide(slide) { // lógica de exibir slide...
// sincroniza dispositivos ws.send(slide.id); } 

Repare como usei um protocolo bem simples, trocando apenas os IDs dos slides. Isso facilitou em ocupar menos banda e evitar deixar a rede pesada no momento da palestra.

Banner da Imersão de IA da Alura com Google Gemini. Participe de aulas gratuitas online com certificado. Domine as inovações mais recentes da IA.

Servidor WebSockets com Jetty 8

O cliente JavaScript é bastante simples, mas a complexidade maior acaba ficando no servidor. WebSockets são um novo protocolo de comunicação em cima do HTTP e porta 80, e portanto exigem um servidor compatível. Há uma implementação de WebSockets muito boa no Jetty 8 para usarmos em Java, mas há outras para diversas linguagens - como o socket.io para Node.JS.

Com Jetty 8, é possível usar WebSockets através de uma Servlet especial, a WebSocketServlet. Herdamos dessa classe e sobrescrevemos o método doGet como numa Servlet comum. Mas, além disso, devemos sobrescrever o método doWebSocketConnect que faz a conexão no protocolo de WebSockets em si.

Sua implementação desse método deve devolver um objeto do tipo WebSocket que você vai criar. Para trabalhar com mensagens texto - como no nosso caso - implementamos a interface OnTextMessage. Essa interface nos dá três métodos: onOpen, onMessage e onClose.

Servidor de sincronização com Java

A ideia do sincronizador de slides é simples: guardar todos os usuários conectados numa lista e, quando chegar uma nova mensagem, reenviamos pra todo mundo. Para saber qual usuário é o principal, que comanda a sincronização, vou usar um parâmetro na URL:

 @WebServlet(urlPatterns="/sincronizar") public class SincronizadorServlet extends WebSocketServlet { // lista de todos os usuários conectados. // cuidado com acesso concorrente, por isso uso aqui a CopyOnWriteArraySet private final Set<SyncWebSocket> usuarios = new CopyOnWriteArraySet<SyncWebSocket>(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
// implementação do doGet aqui. // pode até deixar em branco, sem resposta.
} @Override public WebSocket doWebSocketConnect(HttpServletRequest request, String arg1) {
// faz a conexão via WebSockets e devolve um novo cliente. // devolvemos um objeto da nossa classe SyncWebSocket que implementa a lógica. // repare que ela terá acesso à lista de usuários e // ao parâmetro para saber se é o usuário principal ou não. return new SyncWebSocket(usuarios, request.getParameter("principal") != null); } } 

Cada cliente é representado pela minha classe SyncWebSocket que recebe as mensagens do usuário principal e dispara para todos os outros.

 public class SyncWebSocket implements OnTextMessage { private final Set<SyncWebSocket> usuarios; private final boolean principal; private Connection connection; public SyncWebSocket(Set<SyncWebSocket> usuarios, boolean principal) { this.usuarios = usuarios; this.principal = principal; }
public void onOpen(Connection connection) { // novo usuário conectado. adicionar na lista compartilhada usuarios.add(this);
// guarda a Connection pra enviar mensagens depois this.connection = connection; } public void onClose(int arg0, String arg1) { // remove usuário da lista quando sai usuarios.remove(this); }
public void onMessage(String message) { // só recebe mensagens se esse for o usuário principal. if (principal) {
// envia a mensagem pra todo mundo, sincronizando os clientes for (SyncWebSocket usuario: usuarios) { try { usuario.connection.sendMessage(message); } catch (IOException e) { usuarios.remove(usuario); usuario.connection.close(); } } } else { throw new RuntimeException("Você não pode mandar mensagens!"); } } } 

Para subir esse código, você vai precisar do Jetty 8. No meu caso, precisei também copiar os JARs de websockets e outras libs do Jetty pra dentro do WEB-INF/lib do projeto.

É um exemplo simples de sincronização, mas muito mais é possível com WebSockets. Dá pra mandar mensagens binárias, e enviar e receber mensagens ao mesmo tempo.

Mais WebSockets e o futuro

WebSockets são suportadas nas últimas versões de todos os navegadores, incluindo os mobile. Há um detalhe apenas com relação à versão do protocolo que é suportada. Tivemos várias revisões do protocolo que foi sendo refinado com o tempo fechando bugs importantes de segurança. A versão final do RFC ainda não é suportada por todo mundo e pode gerar alguns problemas de compatibilidade para coisas mais complicadas.

A transmissão de dados binários foi adicionada recentemente ao protocolo e também não é suportada em todos os navegadores.

Veja outros artigos sobre Front-end