Com a especificação do HTML5, também foi introduzido o WebScoket Javascript, onde é possível abrir um canal de comunicação entre o cliente e o servidor. Tanto o cliente quanto o servidor podem enviar informações um para o outro através do canal aberto, mudando o paradigma do request and response, padrão das aplicações web. Ou seja, o servidor pode enviar informações para o cliente, sem que ele explicitamente tenha solicitado, e vice-versa.
A conexão é feita por meio do protocolo TCP, utilizando a porta 80, o que facilita a utilização e implementação, pois é a porta padrão para acesso a web, e não é bloqueada diretamente no firewall.
O protocolo WebSocket já está sendo suportado pela grande maioria dos browsers atuais como Mozila e Chrome. Como sempre (para surpresa de todos) o IE ficou um pouco atrás, mas nas versões mais atuais é possível que já esteja sendo suportado.
Por fim é interessante saber que cada tecnologia está “correndo” para especificar sua implementação. Do conhecimento do autor já existem implementações em Jetty, Tomcat7, Node.js e Rails, é provável que outras tecnologias já estejam suportando o protocolo WebSocket, mas não queremos dar falsas esperanças, se conhecerem algo mais, favor coloquem nos comentários.
Requisitos
Nesse artigo acompanharemos a implementação de um serviço utilizando WebSocket com Tomcat 7, lembrando que a especificação do WebScoket no Tomcat 7 ainda está em fase de desenvolvimento, então esperem algumas mudanças no futuro próximo.
O que você precisa para esse artigo:
- Eclipse JEE;
- Tomcat 7.
Criando o projeto
Primeiro criaremos um web dinamic project no Eclipse, com o nome de introwebsocket. Para garantir o funcionamento correto, crie uma pagina qualquer e execute no seu servidor Tomcat 7, caso exiba normalmente, prossigamos com o a implementação.
Lembrando a implementação do websocket utiliza a biblioteca do Tomcat, então temos que adicionar a biblioteca do servidor ao nosso projeto, como mostra a Figura 1. Botão direito no projeto > propriedades > java build path > add library > server runtime > Apache Tomcat v7.0.;
Feito isso, criaremos os pacotes br.com.devmedia.servlet e br.com.devmedia.websocket.
Criando o WebSocket
No pacote br.com.devmedia.servlet, criaremos a classe MyWebSocketServlet como mostra a Listagem 1.
@WebServlet("/websocket")
public class MyWebSocketServlet extends WebSocketServlet {
private static final long serialVersionUID = 1L;
private static final List<ConnectionWS> connections = new ArrayList<ConnectionWS>();
@Override
protected StreamInbound createWebSocketInbound(String subProtocol,
HttpServletRequest request) {
String username = request.getParameter("username");
return new ConnectionWS(username);
}
public static final List<ConnectionWS> getConnections(){
return connections;
}
public static final void broadcast(String message){
for (ConnectionWS con : MyWebSocketServlet.getConnections()) {
try {
con.getWsOutbound().writeTextMessage( CharBuffer.wrap(message) ) ;
System.out.println("Enviando mensagem de texto (" + message + ")");
} catch (IOException ioe) {
System.out.println("Aconteceu um erro");
}
}
}
}
Veja que a classe “ MyWebSocketServlet deve estender WebSocketServlet, que é uma classe da biblioteca do Tomcat 7. Caso não esteja conseguindo importar a classe, verifique o Java Build Path.
Ao estender a classe WebSocketServlet, deverá ser implementado o método creteWebSocketInbound, que basicamente retorna a criação de uma conexão. Esse método será chamado sempre que um novo cliente se conectar ao WebSocket. Veja que o método possui um parâmetro HttpServletRequest, logo é possível passar parâmetros no momento de conexão, e é o que estamos fazendo, passando um nome de usuário para identificar nosso cliente.
Veja que possuímos uma lista de ConnectionWS (veremos a seguir a implementação), basta saber que se trata de uma lista de conexões de clientes.
Criando a classe de conexao
Agora no pacote br.com.devmedia.websocket criaremos a classe ConnectionWS como mostra na listagem 2.
public final class ConnectionWS extends MessageInbound {
private final String username;
public ConnectionWS(String username) {
this.username = username;
}
@Override
protected void onOpen(WsOutbound outbound) {
// Adiciona essa nova conexão a lista de conexões
MyWebSocketServlet.getConnections().add(this);
String message = String.format("\"%s\" se conectou.", username);
MyWebSocketServlet.broadcast(message);
}
@Override
protected void onBinaryMessage(ByteBuffer arg0) throws IOException {
throw new RuntimeException("Metodo não aceito");
}
@Override
protected void onTextMessage(CharBuffer msg) throws IOException {
String message = String.format("\"%s\": %s", username, msg.toString());
MyWebSocketServlet.broadcast(message);
}
}
A classe deve estender MessageInbound e implementar os métodos que estão faltando, vamos também sobrescrever o método onOpen, para que ele adicione a nova conexão à lista de conexões no nosso Servlet, e lançar uma mensagem a todos os cliente conectados avisando que alguém se conectou. Para isso usamos o método broadcast, que recebe uma mensagem como parâmetro, transforma em CharBuffer, e percorre a lista de conexões e enviar esse CharBuffer.
Veja os dois métodos que foram obrigados a serem implementados, onBinaryMessage e onTextMessage, utilizaremos apenas o onTextMessage, então não faremos nenhuma alteração outro método.
Implementado o lado do cliente
Agora que temos o nosso servidor pronto, está na hora de criarmos o lado do cliente, onde ele irá se conectar, enviar e receber as mensagem. No caso estamos utilizando a especificação do HTML5 para WebSockets(você pode acessar o link nas referencias e ler um pouco mais sobre o assunto).
Vamos criar a nossa página index.jsp, como na listagem a seguir.
<!DOCTYPE html>
<html>
<head>
<title>Apache Tomcat WebSocket Examples: Chat</title>
<style type="text/css">
p {
margin: 0;
}
#messageArea {
width: 500px;
height: 200px;
overflow: auto;
border: 1px solid black;
}
#iptMessage {
width: 240px;
}
</style>
</head>
<body>
<h2>Introdução WebSocket</h2>
<div id="messageArea"></div>
<div>
<form onsubmit="return false;">
<input id="iptMessage" name="message" />
<button id="btnEnviar">Enviar</button>
</form>
</div>
<script type="text/javascript">
function log(msg) {
if (typeof console !== "undefined")
console.log(msg);
}
var username = prompt("Conectar como?");
if ('WebSocket' in window) {
var websocket = new WebSocket("ws://" + document.location.host
+ "/introwebsocket/websocket?username=" + username);
} else if ('MozWebSocket' in window) {
var websocket = new WebSocket("ws://" + document.location.host
+ "/introwebsocket/websocket?username=" + username);
} else {
alert("Browser não suporta WebSocket");
}
if (websocket != undefined) {
websocket.onopen = function() {
log("Conectou com sucesso");
};
websocket.onclose = function() {
log("Desconectou com sucesso");
alert("Desconectou com sucesso");
};
websocket.onerror = function() {
log("Aconteceu um erro");
};
websocket.onmessage = function(data) {
log("Recebeu mensagem");
log(data);
var message = "<p>" + data.data + "</p>";
var messageArea = document.getElementById("messageArea");
messageArea.innerHTML += message;
messageArea.scrollTop = messageArea.scrollHeight;
};
function sendMessage(msg) {
log("Enviar mensagem (" + msg + ")");
websocket.send(msg);
}
document.getElementById("btnEnviar").onclick = function() {
var msg = document.getElementById("iptMessage").value;
sendMessage(msg);
return false;
};
}
</script>
</body>
</html>
Criaremos uma “div” com o identificador messageArea, que tera a responsabilidade de receber as mensagem, um formulário de envido de mensagem, para poder se comunicar com outros clientes no websocket, o importante nesse arquivo é o javascript.
Veja que não estamos utilizando nenhuma biblioteca javascript, apenas o bom e velho padrao, ao carregar a pagina é chamado um “prompt” para receber o nome para identificação do cliente como mostra a figura 2, isso é feita a verificação se o browser tem suporte ao WebSocket, caso não possua retorna um alert.
Com a conexão estabelecida, o objeto websocket será iniciado com eventos padrões, onopen que é chamado quando a conexão é estabelecida com o websocket, como mostra a figura 3, o onclose que é chamado ao acontecer uma interrupção de conexão com o websocket, e o onmessage que é responsável por receber as mensagens vindas do servidor.
O WebSocket também possui um método chamado “send”, responsável por enviar mensagem para o servidor, e o servidor se responsabiliza por divulgar a mensagem para as outras conexões, veja a figura 4.
Para testar a conexão com o websocket, tente abrir varias páginas em abas diferentes ou navegadores diferentes (IE não conta).
Conclusão
Nesse artigo vimos uma implementação básica de um WebSocket com Tomcat 7, existem inúmeras outras, cada uma com suas particularidades. As possibilidades dessa nova especificação são bem amplas, e logo todos os browsers estarão dando suporte a essa especificação.