Como funciona
O cabal bidirectional entre o servidor Socket.IO (Node.js) e o cliente Socket.IO (browser, Node.js, ou outra linguagem de programação) é estabilizada com uma conexão WebSocket sempre que possível, e usará a HTTP long-polling como fallback.
O base de código Socket.IO é dividido em duas camadas distintas:
- O plumbing baixo-nível: o que chamamos de Engine.IO, o mecanismo dentro do Socket.IO.
- A API alto-nível: Socket.IO em si.
Engine.IO
Engine.IO é responsavel por estabelecer a conexão de baixo-nivel entre o servidor e o cliente. Ele lida com:
- os varíos transports e o upgrade mechanism
- a detecção de disconecção
O cod;igo fonte pode ser encontrado aqui:
- servidor: https://github.com/socketio/engine.io
- cliente: https://github.com/socketio/engine.io-client
- parser: https://github.com/socketio/engine.io-parser
- descrição de protocolo: https://github.com/socketio/engine.io-protocol
Transports
Existem atualmente dois transportes implementados:
HTTP long-polling
O transporte HTTP long-polling (também simplesmente referido como "polling") consiste em sucessivas solicitações HTTP:
- long-running
GET
requests, para receber dados do servidor - short-running
POST
requests, para enviar dados do servidor
Devido à natureza do transporte, emissões sucessivas podem ser concatenadas e enviadas dentro de uma mesma requisição HTTP.
WebSocket
O transporte WebSocket consite em um WebSocket connection, que fornece um canal de comunicação bidirecional e de baixa latência entre o servidor e o cliente.
Devido à natureza do transporte, cada emissão é enviada em seu próprio quadro WebSocket (algumas emissões podem até resultar em dois quadros WebSocket distintos, mais informações aqui).
Handshake
No início da conexão Engine.IO, o servidor envia algumas informações
{
"sid": "FSDjX-WRwSA4zTZMALqx",
"upgrades": ["websocket"],
"pingInterval": 25000,
"pingTimeout": 20000
}
- O
sid
é o ID da sessão, deve ser incluído no parâmetro de consulta (query parameter)sid
e todas subsequentes requisições HTTP. - O
upgrades
array qe contém uma lista de todos transportes os "melhores" transportes que são suportados pelo servidor. - O
pingInterval
epingTimeout
valores são usados em um mecanismo de heartbeat
Atualizando mecanismos
Por padrão, o cliente estabelece uma conexão com o transporte HTTP long-polling.
Mas, por que?
Embora WebSocket é claramente o melhor que há para estabelecer uma comunicação bidirecional, experiencias mostraram que nem sempre é possivel estabelecer uma conexão WebSocket, devido a proxies corporativos, firewall pessoal, softwares de antivirus...
Do ponto de vista do usuário, uma conexão WebSocket malsucedida pode ser traduzida em até 10 segundos de espera para que o aplicativo em tempo real comece a trocar dados. Isso perceptivelmente prejudica a experiência do usuário.
Para resumir, Engine.IO foca em confiabilidade e experiência do usuário em primeiro lugar, melhorias de UX potenciais marginais e aumentar o desempenho do servidor em segundo lugar.
Para atualizar, o cliente vai:
- certifique-se de que seu buffer de saída esteja vazio -coloque o transporte atual em modo somente leitura
- tente estabelecer uma conexão com o outro transporte
- se for bem sucedido, feche o primeiro transporte
You can check in the Network Monitor of your browser:
- handshake (contém a sessão ID — aqui,
zBjrh...AAAK
— que é usada em uma requisição subsequente) - envio de daods (HTTP long-polling)
- recebimento de dados (HTTP long-polling)
- atualizações (WebSocket)
- recebimento de dados (HTTP long-polling, fechado uma vez que a conexão WebSocket é 4. E estabelecido com sucesso)
Detecção de disconexão
A conexão Engine.IO é considerada encerrada quando:
- um request HTTP (qualquer GET ou POST) falha (por exemplo, quando o servidor é desligado)
- a conexão do WebSocket é encerrada (por exemplo, quando o usuario uma aba em seu browser)
socket.disconnect()
é chamada no lado do servidor ou no lado do cliente.
Há também um mecanismo de heartbeat que verifica se a conexão entre o servidor e o cliente ainda está funcionando:
Em um determinado intervalo (o valor pingInterval
enviado no handshake) o servidor envia um pacote PING e o cliente tem alguns segundos (o valor pingTimeout
) para enviar um pacote PONG de volta. Se o servidor não receber um pacote PONG de volta, ele considerará que a conexão foi encerrada. Por outro lado, se o cliente não receber um pacote PING dentro de pingInterval + pingTimeout
, ele considerará que a conexão foi encerrada.
Os motivos de disconexão são listados aqui (lado do servidor) e aqui (lado do cliente).
Socket.IO
Socket.IO fornece alguns recursos adicionais sobre a conexão Engine.IO:
- Reconexão automatica
- buffer de pacotes
- acknowledgments
- transmissão para todos os clientes ou um subconjunto de clientes (que nós chamamos de "Room")
- multiplexação (aue nós chamamos de "Namespace")
O código fonte pode ser encontrado aqui:
- servidor: https://github.com/socketio/socket.io
- cliente: https://github.com/socketio/socket.io-client
- parser: https://github.com/socketio/socket.io-parser
- descrição de protocolo: https://github.com/socketio/socket.io-protocol