Go

Websockets em Go

Hoje vou mostrar em poucas linhas de código como criar um Websocket usando um pacote padrão no Go (Golang) chamado net/http e a implementação de Websocket da Gorilla Web toolkit. Este post parte do principio que você já tem o setup mínimo Go configurado (instalando o compilador, criado a estrutura de diretórios e declarado a variável […]

Background Image

Hoje vou mostrar em poucas linhas de código como criar um Websocket usando um pacote padrão no Go (Golang) chamado net/http e a implementação de Websocket da Gorilla Web toolkit.

Este post parte do principio que você já tem o setup mínimo Go configurado (instalando o compilador, criado a estrutura de diretórios e declarado a variável GOPATH).

O que são Websockets?

WebSockets é um protocolo de comunicação bidirecional que permite comunicação real-time entre um navegador web e um servidor. Ele foi criado para fornecer uma alternativa mais eficiente às conexões HTTP long polling e HTTP Streaming, que são utilizadas para criar aplicativos com comunicação em tempo real.

Ao contrário das conexões HTTP tradicionais, onde o cliente precisa fazer uma solicitação e o servidor precisa enviar uma resposta, as conexões WebSockets são bidirecionais e permitem que o cliente e o servidor envie mensagens uns aos outros sem precisar de uma solicitação explícita.

Portanto as aplicações se tornam mais interativas e dinâmicas, como jogos online, chats, aplicações de monitoramento em tempo real, etc.

Para estabelecer uma conexão WebSocket, um navegador web envia uma solicitação HTTP de Upgrade para o servidor, indicando que deseja atualizar para uma conexão WebSocket. Assim, se o servidor suportar WebSockets e concordar com a atualização, ele responderá com uma resposta de Upgrade e a conexão será estabelecida.

Após isso, ambos cliente e servidor podem então enviar mensagens uns aos outros através da conexão WebSocket aberta.

WebSockets é suportado por quase todos os navegadores modernos e também tem suporte a várias linguagens de programação no lado do servidor, incluindo Go, Java, C#, Python, e JavaScript.

Implementando Websockets

Passo um: Construindo um servidor Web em Go

Assumindo que você já esteja dentro do diretório da sua aplicação e tenha criando um arquivo main.go, vamos partir da seguinte estrutura:

package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello!")
}

Vamos começar a implementar nosso servidor Web usando  o net/http. Para isso, precisamos declarar um path para a nossa aplicação responder as requisições em um handler para recebê-las e tratá-las:

package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", handler) // Aqui declaramos o path e o Handler
	http.ListenAndServe(":3000", nil) // Aqui ficará a porta em que nosso Web server vai responder
}

// Aqui vamos tratar nossa requisição: o writer irá permitir
// definir o que desejamos escrever de volta para o client que enviou a requisiçao.
func handler(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprintf(writer, "Hello")
}

Neste ponto do código, já possuímos tudo que precisamos para rodar nossa aplicação Web. Rodando um go run main.go, já podemos ver:

Resposta do servidor
Resposta do servidor

Passo dois: Lidando com chamadas via Websocket

Como citei na introdução, vamos usar o pacote Websocket da Gorilla Web Toolkit. Vale dar uma olhada na documentação atualizada para instalar a dependência, mas por hora só precisamos fazer um go get:

go get github.com/gorilla/websocket

A próxima coisa que precisamos é adicionar um websocket.Upgrader ao nosso código. É através dele que vamos receber o socket para lidarmos com as chamadas.


var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}

Uma das propriedades do upgrader é checarmos a origem do tráfego. Por ora, vamos sempre retornar true e assim seguirmos com o post.

Agora vamos alterar nossa função handler para finalmente lermos a mensagem via Websocket, logarmos no console do Web server e, para propósito de demonstração, vamos retornar a mesma mensagem de volta para o cliente:

package main

import (
	"fmt"
	"github.com/gorilla/websocket"
	"net/http"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin:     func(r *http.Request) bool { return true },
}

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":3000", nil)
}

func handler(writer http.ResponseWriter, request *http.Request) {
	socket, err := upgrader.Upgrade(writer, request, nil)
	if err != nil {
		fmt.Println(err)
	}

	for {
		// Vamos ler a mensagem recebida via Websocket
		msgType, msg, err := socket.ReadMessage()
		if err != nil {
			fmt.Println(err)
			return
		}

		// Logando no console do Webserver
		fmt.Println("Mensagem recebida: ", string(msg))

		// Devolvendo a mensagem recebida de volta para o cliente
		err = socket.WriteMessage(msgType, msg)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
}


Para testarmos se tudo funcionou, primeiro vamos levantar o web server via:

go run main.go

Enfim, agora podemos usar Echo Test da websocket.org. Só precisamos alterar a Location apontando para o localhost:3000, como abaixo:

Websocket

Feito isso, podemos conectar no nosso Websocket e enviar mensagens, recebendo as mesmas como resposta. E no console do Web server podemos ver os logs chegando:

Mensagem recebida: Rock it with HTML5 WebSocket

Cuidados ao usar Websockets

Existem alguns cuidados ao usar WebSockets em sua aplicação:

  1. Segurança: Como as conexões WebSockets são bidirecionais e permitem comunicação direta entre cliente e servidor, certamente é importante garantir que a comunicação esteja criptografada para evitar ataques de interceptação de dados. Aliás, isso pode ser feito usando o protocolo HTTPS (HTTP Secure) sobre o WebSocket.

  2. Firewall: Muitos firewalls são configurados para bloquear conexões WebSockets, portanto, é importante garantir que as portas necessárias estejam abertas para sua aplicação.

  3. Estado da conexão: Uma vez que as conexões WebSockets são longas duras, é importante garantir que as conexões não fiquem presas em um estado inválido por muito tempo. Isso pode ser feito implementando uma lógica de tempo limite para as conexões e garantindo que elas sejam fechadas corretamente quando não são mais necessárias.

  4. Escalabilidade: Como as conexões WebSockets são mais intensivas em recursos do que as conexões HTTP tradicionais, é importante garantir que sua aplicação possa escalar para lidar com um grande número de conexões simultaneamente.

  5. Compatibilidade do navegador: WebSockets é suportado por quase todos os navegadores modernos, mas alguns navegadores mais antigos ainda não oferecem suporte completo a ele. Certifique-se de que sua aplicação ofereça uma alternativa para navegadores que não suportam WebSockets.

  6. Fallback: Seu aplicação deve ser preparada para lidar com a situação quando o websocket é desconectado por algum motivo, como por exemplo, mudanças no firewall, ou o usuário sair da página. A solução recomendada é ter um fallback para outra forma de comunicação como o polling ou long polling.

Além disso, é importante monitorar e testar continuamente sua aplicação para garantir que ela esteja funcionando corretamente e para detectar e resolver problemas rapidamente.