WebSockets em Java: quando o HTTP já não é suficiente
\n\nImagine uma bolsa de valores online. Você olha para o gráfico do preço do Bitcoin. A cada segundo, os números mudam. Ou um jogo multiplayer, onde você move o personagem e centenas de outros jogadores veem isso instantaneamente. Ou simplesmente um chat corporativo, onde as mensagens chegam sem precisar apertar F5.
\n\nComo isso funciona nos bastidores? Antigamente, os desenvolvedores se viravam: polling (o cliente pergunta ao servidor a cada segundo "e aí, tem novidades?"), long polling (o servidor mantém a conexão até que haja uma novidade), Server-Sent Events (fluxo unidirecional). Tudo isso são gambiarras. O protocolo HTTP foi originalmente criado como requisição-resposta. Ele não sabe "empurrar" dados sozinho.
\n\nÉ aí que entram os WebSockets. É um protocolo que estabelece uma conexão uma vez — e a mantém aberta. O servidor pode enviar dados ao cliente a qualquer momento, e vice-versa. Comunicação bidirecional em tempo real, sem a sobrecarga dos cabeçalhos HTTP.
\n\nNeste artigo, vamos abordar WebSockets em Java do início ao fim. Vamos escrever um chat completo, analisar as armadilhas e comparar bibliotecas populares. Se você é um desenvolvedor Java e quer criar aplicações em tempo real com Java, está no lugar certo.
\n\nComo funciona o WebSocket? Breve introdução
\n\nWebSocket não é mágica, é apenas um protocolo sobre TCP. Ele começa como uma requisição HTTP comum (handshake) e depois é "atualizado" para um canal bidirecional permanente.
\n\nEtapa 1: Handshake (Aperto de mão)
\nO cliente envia uma requisição HTTP GET comum com o cabeçalho Upgrade: websocket. O servidor responde com o código 101 Switching Protocols. Pronto. A partir deste momento, os dados não trafegam mais como pacotes HTTP, mas como frames WebSocket.
Etapa 2: Frames de dados
\nOs dados são transmitidos em pequenos pacotes (frames). Um frame pode ser de texto (string comum) ou binário (imagem, áudio). As bibliotecas Java escondem essa complexidade — você trabalha simplesmente com strings ou bytes.
\n\nEtapa 3: Fechamento
\nQualquer lado pode iniciar o fechamento da conexão, enviando um frame especial de Close.
\n\nParece simples? E é. Mas o diabo está nos detalhes: timeouts, reconexões, escalabilidade. Mas falaremos disso mais tarde.
\n\nBibliotecas para WebSockets em Java
\n\nNo ecossistema Java, existem três abordagens principais:
\n\n- \n
- Jakarta WebSocket (JSR 356) — padrão Java EE / Jakarta EE. Funciona em qualquer servidor compatível (Tomcat, Jetty, GlassFish). Muitas anotações, pouco código. \n
- Spring WebSocket — uma camada sobre o padrão no ecossistema Spring. Oferece integração com Security, STOMP (protocolo sobre WebSocket para inscrição em tópicos) e templates convenientes. \n
- Netty / Vert.x / Undertow — frameworks assíncronos de baixo nível. Máximo desempenho, mas mais código. \n
Para 90% dos casos, Jakarta ou Spring são suficientes. Vamos analisar ambas as opções com exemplos reais.
\n\nExemplo 1: Chat simples com Jakarta WebSocket (sem Spring)
\n\nSuponha que temos um bom e velho Tomcat 10+ (Jakarta EE). Precisamos de um endpoint no servidor e um cliente (HTML+JS).
\n\nServidor: classe endpoint
\nimport jakarta.websocket.*;\nimport jakarta.websocket.server.ServerEndpoint;\nimport java.io.IOException;\nimport java.util.Set;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\n@ServerEndpoint("/chat")\npublic class ChatEndpoint {\n\n // Armazena todas as sessões ativas (conjunto thread-safe)\n private static final Set<Session> sessions = new CopyOnWriteArraySet<>();\n\n @OnOpen\n public void onOpen(Session session) {\n sessions.add(session);\n broadcast("Usuário conectou. Total no chat: " + sessions.size());\n }\n\n @OnMessage\n public void onMessage(String message, Session session) {\n // Pode-se adicionar lógica: enviar apenas