net
A biblioteca padrão net da linguagem Go é muito poderosa, fornecendo funcionalidades para lidar com comunicação de rede, endereços IP, resolução DNS, protocolos TCP/UDP, protocolo HTTP e outras tarefas comuns. Devido às características de concorrência da própria linguagem Go, ela é muito concisa e eficiente ao lidar com IO de rede.
Resolução de Endereços
Go fornece quatro funções para analisar endereços de rede, explicadas abaixo uma por uma.
Endereço MAC
Assinatura:
func ParseMAC(s string) (hw HardwareAddr, err error)Exemplo:
package main
import (
"fmt"
"net"
)
func main() {
hw, err := net.ParseMAC("00:1A:2B:3C:4D:5E")
if err != nil {
panic(err)
}
fmt.Println(hw)
}CIDR
Assinatura:
func ParseCIDR(s string) (IP, *IPNet, error)Exemplo:
package main
import (
"fmt"
"log"
"net"
)
func main() {
ipv4Addr, ipv4Net, err := net.ParseCIDR("192.0.2.1/24")
if err != nil {
log.Fatal(err)
}
fmt.Println(ipv4Addr)
fmt.Println(ipv4Net)
}Endereço IP
O endereço IP suporta análise de ipv4 e ipv6. A assinatura da função é a seguinte:
func ResolveIPAddr(network, address string) (*IPAddr, error)Exemplo de uso:
package main
import (
"fmt"
"net"
)
func main() {
ipv4Addr, err := net.ResolveIPAddr("ip4", "192.168.2.1")
if err != nil {
panic(err)
}
fmt.Println(ipv4Addr)
ipv6Addr, err := net.ResolveIPAddr("ip6", "2001:0db8:85a3:0000:0000:8a2e:0370:7334")
if err != nil {
panic(err)
}
fmt.Println(ipv6Addr)
}Endereço TCP
O endereço TCP suporta tcp4 e tcp6. A assinatura é a seguinte:
func ResolveTCPAddr(network, address string) (*TCPAddr, error)Exemplo de uso:
package main
import (
"fmt"
"net"
)
func main() {
tcp4Addr, err := net.ResolveTCPAddr("tcp4", "0.0.0.0:2020")
if err != nil {
panic(err)
}
fmt.Println(tcp4Addr)
tcp6Addr, err := net.ResolveTCPAddr("tcp6", "[::1]:8080")
if err != nil {
panic(err)
}
fmt.Println(tcp6Addr)
}Endereço UDP
O endereço UDP suporta udp4 e udp6. A assinatura é a seguinte:
func ResolveUDPAddr(network, address string) (*UDPAddr, error)Exemplo de uso:
package main
import (
"fmt"
"net"
)
func main() {
udp4Addr, err := net.ResolveUDPAddr("udp4", "0.0.0.0:2020")
if err != nil {
panic(err)
}
fmt.Println(udp4Addr)
udp6Addr, err := net.ResolveUDPAddr("udp6", "[::1]:8080")
if err != nil {
panic(err)
}
fmt.Println(udp6Addr)
}Endereço Unix
O endereço Unix suporta unix, unixgram e unixpacket. A assinatura é a seguinte:
func ResolveUnixAddr(network, address string) (*UnixAddr, error)Exemplo de uso:
package main
import (
"fmt"
"net"
)
func main() {
unixAddr, err := net.ResolveUnixAddr("unix", "/tmp/mysocket")
if err != nil {
panic(err)
}
fmt.Println(unixAddr)
}DNS
Go também fornece muitas funções para consultas DNS. O exemplo abaixo é para analisar o endereço IP de um domínio:
package main
import (
"fmt"
"net"
)
func main() {
addrs, err := net.LookupHost("github.com")
if err != nil {
panic(err)
}
fmt.Println(addrs)
}Consultar registros MX:
package main
import (
"fmt"
"net"
)
func main() {
mxs, err := net.LookupMX("github.com")
if err != nil {
panic(err)
}
fmt.Println(mxs)
}Programação de Rede
A lógica de programação TCP é muito simples. Para o cliente, é:
- Estabelecer conexão
- Enviar dados ou ler dados
- Sair
Para o servidor, é:
- Escutar endereço
- Obter conexão
- Criar uma goroutine para lidar com essa conexão
Abaixo está um exemplo simples, código do cliente:
package main
import (
"net"
)
func main() {
// Estabelecer conexão
conn, err := net.Dial("tcp", "0.0.0.0:1234")
if err != nil {
panic(err)
}
defer conn.Close()
// Enviar dados
for i := range 10 {
_, err := conn.Write([]byte{'a' + byte(i)})
if err != nil {
panic(err)
}
}
}Código do servidor:
package main
import (
"errors"
"fmt"
"io"
"net"
"sync"
)
func main() {
// Escutar endereço
listener, err := net.Listen("tcp", "0.0.0.0:1234")
if err != nil {
panic(err)
}
defer listener.Close()
var wg sync.WaitGroup
for {
// Bloquear aguardando o próximo estabelecimento de conexão
conn, err := listener.Accept()
if err != nil {
panic(err)
}
// Iniciar uma nova goroutine para lidar com essa conexão de forma assíncrona
wg.Add(1)
go func() {
defer wg.Done()
buf := make([]byte, 4096)
for {
// Ler dados da conexão
n, err := conn.Read(buf)
if errors.Is(err, io.EOF) {
break
} else if err != nil {
panic(err)
}
data := string(buf[:n])
fmt.Println(data)
}
}()
}
wg.Wait()
}O cliente envia dados e o servidor recebe dados. Este exemplo é muito simples. Quando o servidor estabelece uma nova conexão, basta iniciar uma nova goroutine para lidar com ela, sem bloqueio. A escrita para UDP é basicamente semelhante.
