Back-End

26 jan, 2017

Trabalhando com o protocolo TCP/IP usando Go

Publicidade

Go é uma linguagem open source lançada, em 2009, pelo Google e criada por Robert Griesemer, Rob Pike e Ken Thompson.

Uma das coisas mais bacanas de Go é poder escrever código de forma simples, pouco verborrágica, estruturada e bastante eficaz, sem contar que é uma linguagem fortemente tipada, incrivelmente leve e suporta concorrência nativamente através de goroutines.

Dentre muitos pacotes, Go através do pacote “net” provê interfaces de acesso a I/O, incluindo a pilha TCP/IP, UDP, resolução por nome de domínio e UNIX Sockets.

Com uma ideia muito simples, neste artigo, iremos criar um cliente e um servidor TCP/IP que servirá para observarmos os principais aspectos destas implementações.

Para tal, vamos estabelecer um protocolo de comunicação bastante básico entre as duas aplicações para garantir que ambos, cliente e servidor, consigam executar suas tarefas.

Servidor

O protocolo de comunicação que nosso servidor deverá trabalhar será este:

  • Ouvir a interface tcp na porta 8081;
  • Aceitar conexões;
  • Dentro de um loop infinito, ouvir as mensagens a serem transmitidas pelo cliente;
  • Escrever no terminal estas mensagens;
  • Devolver a mensagem recebida ao cliente (eco).
package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"strings"
)

func main() {

	fmt.Println("Servidor aguardando conexões...")

	// ouvindo na porta 8081 via protocolo tcp/ip
	ln, erro1 := net.Listen("tcp", ":8081")
	if erro1 != nil {
		fmt.Println(erro1)
		/* Neste nosso exemplo vamos convencionar que a saída 3 está reservada para erros de conexão.
		IMPORTANTE: defers não serão executados quando utilizamos os.Exit() e a saída será imediata */
		os.Exit(3)
	}

	// aceitando conexões
	conexao, erro2 := ln.Accept()
	if erro2 != nil {
		fmt.Println(erro2)
		os.Exit(3)
	}

	defer ln.Close()

	fmt.Println("Conexão aceita...")
	// rodando loop contínuo (até que ctrl-c seja acionado)
	for {
		// Assim que receber o controle de nova linha (\n), processa a mensagem recebida
		mensagem, erro3 := bufio.NewReader(conexao).ReadString('\n')
		if erro3 != nil {
			fmt.Println(erro3)
			os.Exit(3)
		}

		// escreve no terminal a mensagem recebida
		fmt.Print("Mensagem recebida:", string(mensagem))

		// para um exemplo simples de processamento, converte a mensagem recebida para caixa alta
		novamensagem := strings.ToUpper(mensagem)

		// envia a mensagem processada de volta ao cliente
		conexao.Write([]byte(novamensagem + "\n"))
	}
}

Cliente

O protocolo de comunicação que nosso cliente deverá trabalhar será este:

  • Conectar-se a interface tcp localhost na porta 8081;
  • Dentro de um loop infinito, realizar leitura do terminal;
  • Escrever no socket a mensagem digitada no terminal (transmitir);
  • Ouvir o retorno do servidor;
  • Escrever no terminal a mensagen retornada (eco).
package main

import "net"
import "fmt"
import "bufio"
import "os"

func main() {

	// conectando na porta 8081 via protocolo tcp/ip na máquina local
	conexao, erro1 := net.Dial("tcp", "127.0.0.1:8081")
	if erro1 != nil {
		fmt.Println(erro1)
		os.Exit(3)
	}

	for {
		// lendo entrada do terminal
		leitor := bufio.NewReader(os.Stdin)
		fmt.Print("texto a ser enviado: ")
		texto, erro2 := leitor.ReadString('\n')
		if erro2 != nil {
			fmt.Println(erro2)
			os.Exit(3)
		}

		// escrevendo a mensagem na conexão (socket)
		fmt.Fprintf(conexao, texto+"\n")

		// ouvindo a resposta do servidor (eco)
		mensagem, err3 := bufio.NewReader(conexao).ReadString('\n')
		if err3 != nil {
			fmt.Println(err3)
			os.Exit(3)
		}
		// escrevendo a resposta do servidor no terminal
		fmt.Print("Resposta do servidor: " + mensagem)
	}
}

Para ter uma boa experiência deste exemplo, execute primeiramente o tcp-server e, em seguida, o tcp-client preferencialmente em terminais diferentes para acompanhar o resultado.

Veja o resultado:

Servidor:

Cliente:

Servidor:

Cliente / Servidor:

Obviamente existem tratamentos que devem ser realizados quanto a quebra de protocolo, exceções decorrentes do processamento das mensagens ou perda de conexão por uma das partes, mas… isso é com vocês!

Conclusão, Go é muito divertido!

Para ajudar, confira os links abaixo: