Web-socket-php

A web como conhecemos é baseada em solicitação/resposta de HTTP, ou seja, em cada site que você acessa, em cada solicitação AJAX que é feita ao servidor, temos uma alocação de um novo processo HTTP que entrará em uma fila pra ser respondido e o tempo de retorno pode variar de acordo com a capacidade do servidor em lidar com esses processos, sejam eles paralelos ou concorrentes, requisições simples ou requisições que dependam de uma grande capacidade computacional.
Agora quando pensamos em uma aplicação de respostas em tempo real, logo nos deparamos com um problema, chamado alta latência, pois, precisaremos consultar nosso servidor muito mais vezes, isso sem contar no número de clientes ativos fazendo requisições ao mesmo recurso ou áreas diferentes do mesmo sistema, causando lentidão no servidor e em casos mais graves, teremos problemas como timeout de execução ou queda do servidor.
Event loop

Toda aplicação realtime, necessariamente possui um loop de consulta de estado ou evento, isso pode estar tanto do lado cliente como do lado servidor. Esse loop ficará escutando a aplicação aguardando alguma mudança de estado, e caso haja atualização, o sistema deve enviar esses dados, a diferença entre o loop do cliente e o do servidor é que no caso do servidor temos um único serviço que irá ficar escutando todas as requisições e mandando as mudanças em tempo real para todos ou algum cliente específico via socket, já no caso do cliente, teremos um script long pooling em todos os clientes, que irá consultar periodicamente o servidor e receber as possíveis alterações do servidor.
Primeiro socket PHP
Para criar um socket com php, você deve seguir 6 passos, que vão da abertura de um socket até remover a mascara da mensagem resolver seu encode.
Passos:
- Abrir um socket.
- Vincular a um endereço.
- Escutar conexões de entrada
- Aceitar conexões
- WebSocket Handshake.
Passo 1 – Abrir um socket:
Primeiro vamos criar um socket com socket_create(Domain, Type, Protocol) do PHP:
1
| |
Passo 2 – Vincular a um endereço:
socket_bind() recebe como parâmetro o resource do socket que já foi criado com o socket_create(), o endereço e opcionalmente a porta. Isto tem que ser feito antes que uma conexão seja estabelecida, usando socket_connect() ou socket_listen().
1
| |
Passo 3 – Escutar conexões de entrada
Após o socket ter sido criado usando socket_create e associado para um nome com socket_bind() , ele deve dizer para aguardar por escuta em conexões que irão entrar socket com o socket_listen().
1
| |
Passo 4 – Aceitar conexões
Após o socket ter sido criado usando socket_create(), passar um nome com socket_bind(), e dizer para listar conexões com socket_listen(), a função socket_accept() irá aceitar conexões vindas neste socket. Uma vez que uma conexão é feita com sucesso , um novo “resource” do socket é retornado, que deve ser usado para comunicação. Se há múltiplas conexões na fila do socket, a primeira irá ser usada. Se não há conexões pendentes, socket_accept() irá bloquear até que uma conexão esteja presente.
1
| |
Passo 5 – WebSocket Handshake
O cliente tem de apresentar-se, enviando uma solicitação WebSocket handshake para estabelecer uma conexão bem sucedida com o servidor, o pedido contém um Sec-WebSocket-Key que é uma chave em base64 de 16 bytes
1 2 3 4 5 6 7 8 9 | |
Passo 5 – WebSocket Handshake
Depois de fazer handshaking, o cliente pode enviar e receber mensagens, mas as mensagens enviadas são todas criptografadas, por isso, se queremos exibi-las, precisamos remover a mascara de cada frame de dado, conforme o rfc6455.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | |
Executar
Para executar o Socket basta você acessar o seu terminal e executar o script que serve como servidor, pois se inicia-lo via browser, o seu socket não funcionará.
1
| |
Veja o Exemplo completo no github.
O React PHP
O React PHP é uma biblioteca que foi criada para suprir uma necessidade crescente de computação reativa, ela fornece entre outras coisas a possibilidade de criar aplicações com IO não blocante, como o NodeJS.
Um chat com React e Ratchet
O Rachet possui um conjunto de soluções que abstraem e facilitam a criação, manutenção, envios e recebimentos de mensagens de um socket. Você terá que gerenciar os eventos do seu socket, com as funções.
Funções
- onOpen
- onMessage
- onClose
- onError
onOpen
Essa função é chamada assim que ocorre uma nova conexão, nela você usa um método para guardar a nova conexão para enviar mensagens.
1 2 3 4 5 6 | |
onMessage
Essa função é chamada quando o evento de existe uma nova mensagem, e com ela, você pode encaminhar essas mensagens para todos ou para alguns clientes conectados ao socket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
onClose
Essa função é chamada assim que um um cliente se desconecta do websocket, e a partir dele, você pode atribuir callbacks para esse evento.
1 2 3 4 5 6 | |
onError
Esse evento é disparado quando ocorre um erro interno no servidor.
1 2 3 4 5 | |
Executar
Para executar o Socket basta você acessar o seu terminal e executar o script que serve como servidor, pois se inicia-lo via browser, o seu socket não funcionará.
1
| |
Veja o Exemplo completo no github.
Javascript
Uma vez criado o socket, você pode acessa-lo do seu browser, lembrando que para acessa-lo de forma nativa, só browsers modernos suportem a API websocket, do contrário, existem diversas saídas, como flashsocket e javasocket, no qual você pode acessa-lo do seu script.
Assim como no caso do servidor, teremos funções com callbacks para todos os eventos de um socket, que são:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Eu fiz um exemplo completo usando Angular js, que encontra-se no github.
Vantagens e desvantagens
A vantagem de usar um websoket é que ao invés de termos muitos clientes fazendo requisições periódicas http ao mesmo endpoint para verificar possíveis mudanças de estado da aplicação, o socket que irá gerenciar as alterações e mandar para o(os) clientes essas atualizações logo que elas ocorrerem, evitando um tráfego desnecessário de requisições http, como consultas de estado sem mudança ou ainda muitos clientes consultando a mesma coisa. Em contra partida, teremos um script executando um loop infinito no servidor, aguardando algum evento para transmitir para os usuários, ou seja, embora você resolva a latência, você terá um processo que irá consumir recursos do servidor durante todo tempo de que você mante-lo rodando, independente se houver ou não novos eventos, solicitações ou usuários ativos.
Além disso, podemos ter situações onde o navegador do cliente não tenha suporte websoket, flash ou java. Nesses casos, não teremos como evitar o uso de long pooling.
O Websoket é o futuro, porém, o PHP não é a melhor saída para esse tipo de recurso, embora funcione bem na maioria dos casos. Eu aconselho o uso de Erlang ou Node JS, por serem linguagens de natureza não blocante e por lidarem melhor com concorrência.
Aplicações
Os websockets podem ter muitas aplicações, sobre tudo, em:
- Chats
- Games multi-player online
- Sistemas de sincronização mobile/Web
Referências
HTML5 Rocks – Apresentando WebSockets – PT
Sergio Lopes – SPYD e HTTP 2.0 – PT
JustCode – Otimizando Performance com Compactação Gzip/Deflate