Você está na página 1de 7

Centro de Pesquisas de Energia Elétrica - CEPEL

Nota Técnica – DGA – 1182/23

Título: Nº de Páginas: 07
Utilização da biblioteca socket.io para implementação de comunicação
bidirecional Websocket entre um Servidor Web e uma Página Web Cliente.

Área emissora: Departamento de Tecnologia em Gestão de Ativos – DGA

Objetivo/Resumo:
Esta NT documenta a implementação de um Webserver com os protocolos HTTP e Websocket em
Python, e de uma página cliente em HTML e Javascript com comunicação bidirecional utilizando as
implementações da biblioteca socket.io em Python e Javascript.

Autores: Palavras-Chave:
André Tomaz de Carvalho GAMMA, socketio, Python, HTTP, Javascript,
websockets, webserver.

Classificação de acesso:

Público Interno Setorial Confidencial

Responsável pela Nota Técnica

Assinado de forma digital


por ANDRE TOMAZ DE
CARVALHO:41921852372
Dados: 2023.04.03
09:32:15 -03'00'

Tel.: (21) 2598 – 6343

E-mail: tomaz@cepel.br

C e n t r o d e P e s q u i s a s d e E n e r g i a E l é t r i c a – C E P E L www.cepel.br
Sede: Av. Horácio Macedo, 354 - Cidade Universitária - CEP 21941-911 - Rio de Janeiro - RJ - Brasil - Tel.: 21 2598-6000
Unidade Adrianópolis: Av. Olinda, 5800 - Adrianópolis - CEP 26053-121 - Nova Iguaçu - RJ - Brasil - Tel.: 21 2666-6200
Endereço Postal: CEPEL Caixa Postal 68007 - CEP 21941-971 - Rio de Janeiro - RJ - Brasil / Endereço Eletrônico: cepel@cepel.br

F-CO-014, REV. 0, 04/10/2021


Centro de Pesquisas de Energia Elétrica - CEPEL
Nota Técnica – DGA – 1182/23

1. Introdução

Esta NT descreve a utilização da biblioteca socketio para implementação de comunicação bidirecional


Websocket entre um webserver e uma Página Web.

A referência [1] mostrou fundamentos para a implementação de aplicações em Javascript


autocontidas em uma página Web. O webserver tem a função primordial de atender a
solicitações do tipo HTTP GET, enviando páginas solicitadas aos clientes.

Frequentemente, no entanto, é conveniente que o código da aplicação interaja com código


executado no servidor. Para isso, é necessário implementar a troca de dados entre o servidor e
a página HTML.

Esta NT descreve como o servidor de HTTP e essa troca bidirecional de mensagens podem ser
implementados a partir das bibliotecas homônimas socketio para Python [2] e socketio para
Javascript [3].

O socketio é uma API que encapsula as funções de um servidor de HTTP e de Websocket,


como sugere a Fig. 1. A página HTML enviada conterá um script que implemente um cliente
Websocket capaz de trocar mensagens bidireccionalmente com o servidor.

GET
http server html

ws server ws client (.js)


ws messages

Webserver Navegador

Fig. 1 – Links para troca de mensagens entre o Webserver e o Navegador.

2. Implementando um Servidor estático de HTTP

A Fig. 2 mostra a implementação em Python de um servidor HTTP de páginas estáticas usando


o socketio.

import eventlet
import socketio

sio = socketio.Server()
app = socketio.WSGIApp(sio, static_files={
'/': {'filename': 'index.html'},
'/socket.io.js': {'filename': 'socket.io.js'},
______________________________________________________________________________
2/7
F-CO-014, REV. 0, 04/10/2021

Classificação da informação: Interno


Centro de Pesquisas de Energia Elétrica - CEPEL
Nota Técnica – DGA – 1182/23

'/socket.io.js.map': {'filename': 'socket.io.js.map'},


'/wsClient.js': {'filename': 'wsClient.js'}
}
)

if __name__ == '__main__':
eventlet.wsgi.server(eventlet.listen(('localhost', 5000)), app)
Fig. 2 – Implementação de um servidor estático de HTTP com socketio.

Ao executar o código, o console exibe a mensagem da Fig. 3.

Fig. 3 – Saída do console para o código da Fig. 2.

O método socketio.WSGIApp() recebe como parâmetro uma estrutura que, a cada caminho
apontado na URL do navegador (‘/’, ou ‘/socket.io.js’, por exemplo), associa quais arquivos
devem ser enviados ao navegador.

Essa lista é um dicionário que pode ser usado para rotear solicitações, isto é, posso enviar um
arquivo com outro nome diferente daquele solicitado na URL.

Ao definir essa lista arquivo por arquivo, todos os arquivos de interesse devem ser incluídos,
incluindo scrips e css. No caso da Fig. 2, como não foi definido uma página para o caminho
http://127.0.0.1:5000/index.html, o servidor retorna uma mensagem ‘not found’. A página
index.html só é enviada se o usuário acessar simplesmente http://127.0.0.1:5000.

Também é possível definir em uma única regra todos os arquivos de uma pasta como estáticos
fazendo, por exemplo:

static_files = {

'/static': './public',

}
Fig. 4 – Definição de todos os arquivos de uma pasta no servidor estático.

Nesse caso, todas as URLs começando com /static/*** retornarão os arquivos de nome ***
contidos na pasta ./public.

Observação. De acordo com a documentação, o servidor de arquivos estáticos do socketio


não deve ser usado em produção, mas apenas para fins de desenvolvimento, pois carece de
recursos como tratamento de erro, etc.

______________________________________________________________________________
3/7
F-CO-014, REV. 0, 04/10/2021

Classificação da informação: Interno


Centro de Pesquisas de Energia Elétrica - CEPEL
Nota Técnica – DGA – 1182/23

3. Implementação de um Servidor de Websocket

O serviço de Websocket já estava incluso no socketio.Server no exemplo anterior. A


implementação do servidor consiste em tratar os eventos associados ao protocolo.

O protocolo Websocket estabelece uma conexão bidirecional de dados entre dois endpoints. O
servidor fica disponível para aceitar conexões de clientes que, uma vez conectados, podem
trocar mensagens com a aplicação.

Toda a API do socketio é baseada em eventos. Assim, o código abaixo define o tratamento de
três eventos distintos:

• Connect – executa ao receber uma conexão de um cliente;


• Disconnect – executa ao fechar a conexão com um cliente;
• echo – executa ao receber um evento do cliente com esse nome.

A troca de mensagens no socketio é realizada através de eventos. No Python, um evento é


gerado pelo método sio.emit(), o qual recebe como parâmetros: o nome do evento e os
dados enviados como parâmetro do próprio evento.

No código da Fig. 5, ao receber um evento de nome ‘echo’, o servidor emite uma mensagem
de retorno, isto é, emite um evento de nome 'clientReceiveTextMessage', retornando ao cliente
os dados enviados.

import eventlet
import socketio

sio = socketio.Server()
app = socketio.WSGIApp(sio, static_files={
'/': {'filename': 'index.html'},
'/socket.io.js': {'filename': 'socket.io.js'},
'/socket.io.js.map': {'filename': 'socket.io.js.map'},
'/wsClient.js': {'filename': 'wsClient.js'}
}
)

@sio.event
def connect(sid, environ): # Executa ao abrir uma conexão
print('connected to ', sid)

@sio.event
def disconnect(sid): # Executa ao fechar uma conexão
print('disconnect ', sid)

@sio.event
def echo(sid, data): # Executa ao receber um evento echo
sio.emit('clientReceiveTextMessage', 'Echoed message: ' + data) # Emite evento de
volta
print('Echoing Message: ', data)

______________________________________________________________________________
4/7
F-CO-014, REV. 0, 04/10/2021

Classificação da informação: Interno


Centro de Pesquisas de Energia Elétrica - CEPEL
Nota Técnica – DGA – 1182/23

if __name__ == '__main__':
eventlet.wsgi.server(eventlet.listen(('localhost', 5000)), app)
Fig. 5 – Código do Servidor de Websocket.

4. Implementação do Cliente em uma Página Web

Desejamos que uma Página Web possa trocar dados com o servidor. Usaremos como exemplo
a página cujo código HTML pode ser visto na Fig. 6.

<!DOCTYPE html>
<html>
<head>
<title>Socket.io WS Client</title>
<style>
body { margin: 0; padding-bottom: 3rem; font-family: -apple-system,
BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }

#form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed;


bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box;
backdrop-filter: blur(10px); }
#input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin:
0.25rem; }
#input:focus { outline: none; }
#form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem;
border-radius: 3px; outline: none; color: #fff; }

#messages { list-style-type: none; margin: 0; padding: 0; }


#messages > li { padding: 0.5rem 1rem; }
#messages > li:nth-child(odd) { background: #efefef; }
</style>
<script src="socket.io.js"></script>
<script src="wsClient.js" async></script>

</head>
<body>
<ul id="messages"></ul>
<div id="form">
<input id="input" autocomplete="off">
<button id="send">Send</button>
</div>
</body>
</html>
Fig. 6 – Código da página index.html.

A página é extremamente simples. Na seção <head> possui definições de estilo CSS, e faz
referência a dois scripts. Na seção <body> define uma lista <ul>, um <input> de texto e um
<button>.

Ao clicar no botão, queremos enviar ao servidor o texto digitado em uma mensagem


Websocket a ser tratada pelo evento echo. As respostas devem ser exibidas na lista <ul>.
______________________________________________________________________________
5/7
F-CO-014, REV. 0, 04/10/2021

Classificação da informação: Interno


Centro de Pesquisas de Energia Elétrica - CEPEL
Nota Técnica – DGA – 1182/23

Essas funcionalidades serão implementadas no script wsClient.js, utilizando a biblioteca


Javascript socket.io.js [3], também carregada como script.

O código do script wsClient.js é mostrado na Fig. 7.

var socket = io("ws://127.0.0.1:5000");

SendMessageButton = document.getElementById("send").addEventListener("click",
sendMessage);

function sendMessage(){
socket.emit("echo", document.getElementById("input").value);
}

socket.on("clientReceiveTextMessage", (data) => {


document.getElementById("messages").innerHTML += '<li>' + data + '</li>'
});
Fig. 7 – Script wsClient.js.

O script wsClient é extremamente simples. A função

socket = io("ws://127.0.0.1:5000")

estabelece a conexão com o servidor. Caso a conexão não seja estabelecida ou seja
interrompida, o socketio tenta se reconectar automaticamente sucessivas vezes até obter
sucesso.

Em seguida, o script associa a função sendMessage() ao evento de click no Botão. A função


sendMessage emite um evento ‘echo’ para o servidor cujo dado é o texto contido no campo
<input>.

Para enviar um evento, o método

socket.emit(event, data);

é utilizado.

Finalmente, o script define um método para tratar eventos do tipo "clientReceiveTextMessage".


Esse método insere os dados enviados como itens da lista <ul>.

A Fig. 8 mostra o frontend da aplicação, que recebe do servidor mensagens de eco para todas
as mensagens enviadas.

______________________________________________________________________________
6/7
F-CO-014, REV. 0, 04/10/2021

Classificação da informação: Interno


Centro de Pesquisas de Energia Elétrica - CEPEL
Nota Técnica – DGA – 1182/23

Fig. 8 – Página da Aplicação Websocket Client.

5. Referências

[1] Nota Técnica DGA 1069/23 - Prototipação de Aplicações Simples com HTML e Javascript.

[2] Biblioteca Python socket.io: https://python-socketio.readthedocs.io/en/latest/index.html

[3] Biblioteca Javascript cliente socket.io: https://github.com/socketio/socket.io-client

______________________________________________________________________________
7/7
F-CO-014, REV. 0, 04/10/2021

Classificação da informação: Interno

Você também pode gostar