Dev (Back & Front)ARTIGO

Unix Domain Socket com Golang

Unix Domain Sockets ou IPC socket é uma forma muito prática e segura de trocar informações entre processos. Neste artigo, Cesar Gimenes mostra como usá-los corretamente.

Unix Domain Socket com Golang
Imagem: Cesar Gimenes

Unix domain socket

Unix Domain Sockets ou IPC socket é uma forma muito prática e segura de trocar informações entre processos. Essa forma de IPC usa um arquivo como endereço/name space no lugar de um IP e uma porta, como seria em uma comunicação via rede.

Uma coisa importante para ter em mente é que como vamos usar um arquivo, o servidor é responsável por ele, e se não existir ele será criado automaticamente, mas se não existir você vai receber um erro com algo como “bind: address already in use”, que significa que o arquivo já existe e o servidor não tem como reaproveitar um arquivo que já existe.

O correto é fazer shutdown elegantemente, fechar e apagar o arquivo antes de derrubar o servidor. E dependendo do sistema, pode ser interessante verificar se o arquivo já existe e apagar antes de subir o servidor.

Apesar da facilidade, como usamos um arquivo como endereço, não dá pra usar pra trocar informação entre maquinas diferentes, e quem fica responsável por manter essa comunicação é o kernel.

O arquivo é apenas um name space, e nenhum byte será escrito no arquivo – ele vai ocupar zero espaço de disco, já que toda a comunicação acontece na RAM e é gerenciada pelo kernel.

Outra coisa muito importante é que: usar Unix domain socket é um recurso padrão de qualquer ambiente POSIX, mas não está presente por padrão no Windows.

Exemplos

Servidor

A conexão de cliente e servidor é muito parecida com a que estamos acostumados quando conectamos via TCP/IP. Basicamente ficamos ouvindo o namespace indicado pelo arquivo e esperamos por conexões como ficaríamos ouvindo uma porta TCP.

Daí, quando essa conexão chega, usamos a função Accept do pacote net e passamos para uma goroutine com o handler dessa conexão.

package main

import (
	"fmt"
	"io"
	"net"
)

func main() {
	l, err := net.Listen("unix", "/tmp/echo.sock")
	if err != nil {
		panic(err)
	}

	for {
		f, err := l.Accept()
		if err != nil {
			panic(err)
		}

		go func(c io.ReadWriter) {
			for {
				buf := make([]byte, 512)
				n, err := c.Read(buf)
				if err != nil {
					return
				}

				fmt.Printf("echo: %s\n", buf[:n])
				_, err = c.Write(buf[:n])
				if err != nil {
					panic(err)
				}
			}
		}(f)
	}
}

Cliente

No nosso exemplo de cliente criamos duas linhas de processamento: uma é a goroutine, que vai ficar reatando tudo que vier pela conexão, e a outra é a própria função main, que ficará em loop enviando dados para o servidor.

Não precisa ser assim. Dependendo de como você quer que seu sistema funcione, você pode, por exemplo, enviar uma mensagem para o servidor e subir uma goroutine para tratar apenas do timeout. O único cuidado é que essas funções bloqueiam o processamento.

package main

import (
	"fmt"
	"io"
	"net"
	"time"
)

func main() {
	f, err := net.Dial("unix", "/tmp/echo.sock")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	go func(r io.Reader) {
		buf := make([]byte, 1024)
		for {
			n, errf := r.Read(buf)
			if errf != nil {
				panic(err)
			}
			fmt.Printf("recebido: %s\n", buf[:n])
		}
	}(f)

	for {
		data := []byte("olá mundo")
		fmt.Printf("enviando: %s\n", data)
		_, err = f.Write([]byte("olá mundo"))
		if err != nil {
			panic(err)
		}

		time.Sleep(time.Duration(400) * time.Millisecond)
	}
}

Servidor usando netcat

Para testes podemos usar também o netcat:

bash
nc -lU /tmp/echo.sock && rm /tmp/echo.sock

Cliente usando netcat:

bash
nc -U /tmp/echo.sock

Exemplos

Executando o servidor echo:

go run echo/server/main.go

Em outro console, execute:

bash
go run echo/client/main.go

Implementação do gonf usando npipe no Windows e Unix Domain Socket: https://github.com/gofn/gofn/blob/master/provision/docker_windows.go / https://github.com/gofn/gofn/blob/master/provision/docker_unix.go

Nossos encontros ocorrem todas as quintas, às 22h00, para participar entre no canal de Go no Telegram.

Cesar GimenesAutor

Trabalha com tecnologia desde a década de 90. Já atuou na área de educação e participou de projetos de mobilidade de grande volume para laboratórios farmacêuticos. Criou games tanto para PC, como para iOS. Hoje está direcionando seus esforços em plataformas de Sistemas Embarcados, IoT, microservices e cloud computing. É um entusiasta de tecnologias como Golang e Docker.

Ver perfil