Mudanças entre as edições de "Go: Programação Distribuída"
Linha 312: | Linha 312: | ||
} | } | ||
} | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | = Exemplo de Computação Distribuída = | ||
+ | |||
+ | == Servidor == | ||
+ | |||
+ | <syntaxhighlight lang=go line> | ||
+ | package main | ||
+ | |||
+ | import ( | ||
+ | "bufio" | ||
+ | "encoding/json" | ||
+ | "fmt" | ||
+ | "net" | ||
+ | "os" | ||
+ | "strconv" | ||
+ | ) | ||
+ | |||
+ | type Message struct { | ||
+ | Begin int `json:"begin"` | ||
+ | End int `json:"end"` | ||
+ | } | ||
+ | |||
+ | func child(conn net.Conn) { | ||
+ | defer conn.Close() | ||
+ | netData, err := bufio.NewReader(conn).ReadString('\n') | ||
+ | if err != nil { | ||
+ | fmt.Println(err) | ||
+ | return | ||
+ | } | ||
+ | msg := Message{} | ||
+ | json.Unmarshal([]byte(netData), &msg) | ||
+ | out := 1 | ||
+ | for i := msg.Begin; i <= msg.End; i++ { | ||
+ | out *= i | ||
+ | } | ||
+ | fmt.Println(msg.Begin, "->", msg.End, ":", out) | ||
+ | conn.Write([]byte(strconv.Itoa(out))) | ||
+ | } | ||
+ | |||
+ | func main() { | ||
+ | arguments := os.Args | ||
+ | if len(arguments) == 1 { | ||
+ | fmt.Println("Enter with port number in argument") | ||
+ | return | ||
+ | } | ||
+ | l, err := net.Listen("tcp", ":"+arguments[1]) | ||
+ | if err != nil { | ||
+ | fmt.Println(err) | ||
+ | return | ||
+ | } | ||
+ | defer l.Close() | ||
+ | fmt.Println("TCP Server initialized at port", arguments[1]) | ||
+ | |||
+ | for { | ||
+ | c, err := l.Accept() | ||
+ | if err != nil { | ||
+ | fmt.Println(err) | ||
+ | return | ||
+ | } | ||
+ | go child(c) | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Cliente == | ||
+ | |||
+ | <syntaxhighlight lang=go line> | ||
+ | package main | ||
+ | |||
+ | import ( | ||
+ | "bufio" | ||
+ | "encoding/json" | ||
+ | "fmt" | ||
+ | "net" | ||
+ | "os" | ||
+ | "strconv" | ||
+ | "sync" | ||
+ | ) | ||
+ | |||
+ | type Message struct { // Estrutura da mensagem | ||
+ | Begin int `json:"begin"` | ||
+ | End int `json:"end"` | ||
+ | } | ||
+ | |||
+ | func connect_server(wg *sync.WaitGroup, ch chan int, server string, begin, end int) { | ||
+ | defer wg.Done() | ||
+ | c, err := net.Dial("tcp", server) // Conecta ao servidor | ||
+ | if err != nil { | ||
+ | fmt.Println(err) | ||
+ | return | ||
+ | } | ||
+ | defer c.Close() | ||
+ | msg := Message{begin, end} // cria uma mensagem na estrutura definida | ||
+ | msgJson, _ := json.Marshal(msg) // transforma em string json | ||
+ | fmt.Fprintf(c, string(msgJson)+"\n") // envia ao servidor | ||
+ | message, _ := bufio.NewReader(c).ReadString('\n') // aguarda a resposta | ||
+ | result, err := strconv.Atoi(message) // transforma a resposta para inteiro | ||
+ | if err == nil { | ||
+ | fmt.Println(begin, "->", end, ":", result) | ||
+ | ch <- result // manda o resultado para o programa principal | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /* | ||
+ | EXECUÇÃO: | ||
+ | go run fat_dist_cli.go [value] [rotines] [server:port] ... | ||
+ | |||
+ | 1: Valor para calcular o fatorial | ||
+ | 2: Quantidade de gorotines, ou conexões a servidores | ||
+ | 3 em diante: servidores para se conectar no formato servidor:porta | ||
+ | |||
+ | EXEMPLO: | ||
+ | |||
+ | go run fat_dist_cli.go 20 5 localhost:4400 localhost:4402 localhost:4401 | ||
+ | |||
+ | O exemplo acima pede para calcular o fatorial de 20, dividindo em 5 | ||
+ | subrotinas. Em sequência temos 3 servidores para utilizar. Como o número | ||
+ | de servidores é menor do que a quantidade de subrotinas, dois deles vão receber | ||
+ | duas requisições de cálculos | ||
+ | */ | ||
+ | |||
+ | func main() { | ||
+ | args := os.Args // pega os argumentos | ||
+ | value, _ := strconv.Atoi(args[1]) // pega o valor | ||
+ | rotines, _ := strconv.Atoi(args[2]) // pega a quantidade de rotinas | ||
+ | size := int(value / rotines) // calcula o tamanho do bloco a separar | ||
+ | servers := make([]string, len(args)-3) // cria o vetor de servidores | ||
+ | j := 0 // | ||
+ | for i := 3; i < len(args); i++ { // coloca os parâmetros como servidores | ||
+ | servers[j] = args[i] | ||
+ | j++ | ||
+ | } | ||
+ | |||
+ | var wg sync.WaitGroup | ||
+ | wg.Add(rotines) | ||
+ | ch := make(chan int, 10) | ||
+ | |||
+ | j = 0 | ||
+ | end := 0 | ||
+ | for i := 0; i < rotines; i++ { // cria as subrotinas | ||
+ | begin := end + 1 // separa o inicio | ||
+ | end = begin + size // e o fim do bloco de cálculo | ||
+ | if end > value { // Não pode passar do valor total | ||
+ | end = value | ||
+ | } | ||
+ | go connect_server(&wg, ch, servers[j], begin, end) // envia para um servidor | ||
+ | j++ | ||
+ | if j >= len(servers) { | ||
+ | j = 0 | ||
+ | } | ||
+ | } | ||
+ | wg.Wait() // aguarda os servidores terminarem | ||
+ | close(ch) | ||
+ | // Pega os resultados e multiplica-os | ||
+ | result := 1 | ||
+ | for v := range ch { | ||
+ | result = result * v | ||
+ | } | ||
+ | // apresenta o resultado final | ||
+ | fmt.Println("Fatorial de", value, "é", result) | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> |
Edição das 17h25min de 4 de novembro de 2021
TCP Cliente
Efetua uma conexão TCP com um servidor. Aguarda o usuário digitar mensagens e dar ENTER. Para saír, basta digitar EXIT.
1package main
2
3import (
4 "bufio"
5 "fmt"
6 "net"
7 "os"
8 "strings"
9)
10
11func main() {
12 arguments := os.Args // Pega os argumentos da linha de comando
13 if len(arguments) == 1 { // Se não tiver argumentos retorna erro
14 fmt.Println("Enter with arguments host:port.")
15 return
16 }
17
18 // Usa os argumentos e se conecta ao servidor host:port
19 c, err := net.Dial("tcp", arguments[1])
20 if err != nil {
21 fmt.Println(err)
22 return
23 }
24 defer c.Close()
25 fmt.Print("Digit your message and press key ENTER to send.")
26 fmt.Println("To exit, digit EXIT and press key exit.")
27 for {
28 reader := bufio.NewReader(os.Stdin) // Prepara o buffer de leitura
29 fmt.Print("MSG: ")
30 text, _ := reader.ReadString('\n') // Le um texto do teclado
31 fmt.Fprintf(c, text+"\n") // Envia o texto pela conexão
32
33 message, _ := bufio.NewReader(c).ReadString('\n') // Aguarda resposta do servidor
34 fmt.Print("RCV: " + message)
35 // Se a resposta for EXIT, fecha a conexão e o cliente
36 if strings.ToUpper(strings.TrimSpace(string(text))) == "EXIT" {
37 fmt.Println("TCP client exiting...")
38 return
39 }
40 }
41}
TCP Servidor Simples
Inicia o servidor e fica aguardando um cliente. Recebe as mensagens do cliente. Quando o cliente envia EXIT, fecha o servidor e a conexão.
1package main
2
3import (
4 "bufio"
5 "fmt"
6 "net"
7 "os"
8 "strings"
9 "time"
10)
11
12func main() {
13 arguments := os.Args // Pega a porta como argumento da linha de comando
14 if len(arguments) == 1 { // Se não for passado a porta dá erro
15 fmt.Println("Enter with port number in argument")
16 return
17 }
18 l, err := net.Listen("tcp", ":"+arguments[1]) // Inicia a conexão TCP na porta
19 if err != nil {
20 fmt.Println(err)
21 return
22 }
23 defer l.Close() // Ao final, fecha a conexão.
24 fmt.Println("TCP Server initialized at port", arguments[1])
25
26 c, err := l.Accept() // Fica aguardando um cliente se conectar
27 if err != nil {
28 fmt.Println(err)
29 return
30 }
31 fmt.Println("Client connected...")
32
33 for { // Laço eterno
34 // Recebe informações no buffer de leitura
35 netData, err := bufio.NewReader(c).ReadString('\n')
36 if err != nil {
37 fmt.Println(err)
38 return
39 }
40 // Se for EXT, fecha o sevidor, mas fecha a conexão antes
41 if strings.ToUpper(strings.TrimSpace(string(netData))) == "EXIT" {
42 fmt.Println("TCP Server terminated...")
43 return
44 }
45
46 // Mostra a mensagem na tela e envia de volta o horário
47 fmt.Print(": ", string(netData))
48 t := time.Now()
49 myTime := t.Format(time.RFC3339) + "\n"
50 c.Write([]byte(myTime)) // Escreve no buffer de escrita para o cliente
51 }
52}
TCP Servidor com múltiplas conexões
Observe que agora temos um servidor diferente. No laço for do main ficamos aguardando um cliente se conectar. Quando houver uma conexão, essa conexão é transferida para uma função child que é chamada concorrentemente como gorotine e continua o laço, aguardando uma nova conexão.
Cada gorotine, ora chamada child, vai ficar responsável por gerenciar uma conexão específica. Quando o cliente mandar a palavra exit, não fecha o servidor, apenas aquela conexão.
Dessa forma, nosso servidor pode trabalhar com múltiplas conexões simultâneas, respondendo concorrentemente há vários clientes.
Veja que no código, quando um cliente manda uma mensagem, escrevemos qual o endereço da conexão para identificar a mensagem e diferenciá-la das mensagens dos outros clientes.
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
"time"
)
func child(conn net.Conn) {
addr := conn.RemoteAddr() // Pega o endereço do cliente
fmt.Println(addr, ": connected...")
defer fmt.Println(addr, ": disconnected...")
defer conn.Close() // Ao final, fecha a conexão
for {
// Recebe informações no buffer de leitura
netData, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
fmt.Println(err)
return
}
// Se for EXIT, fecha fecha a conexão
if strings.ToUpper(strings.TrimSpace(string(netData))) == "EXIT" {
fmt.Println(addr, ": exit...")
return
}
// Mostra a mensagem na tela e envia de volta o horário
fmt.Print(addr, " : ", string(netData))
t := time.Now()
myTime := t.Format(time.RFC3339) + "\n"
conn.Write([]byte(myTime)) // Escreve no buffer de escrita para o cliente
}
}
func main() {
arguments := os.Args // Pega a porta como argumento da linha de comando
if len(arguments) == 1 { // Se não for passado a porta dá erro
fmt.Println("Enter with port number in argument")
return
}
l, err := net.Listen("tcp", ":"+arguments[1]) // Configura uma porta TCP
if err != nil {
fmt.Println(err)
return
}
defer l.Close() // Ao final, fecha a conexão.
fmt.Println("TCP Server initialized at port", arguments[1])
for {
c, err := l.Accept() // Aguarda o próximo cliente se conectar
if err != nil {
fmt.Println(err)
return
}
go child(c) // Passa a conexão para uma gorotine gerenciar
}
}
Daria pra fazer um chat de grupo com isso, não? É um bom exercício.
UDP Cliente
Veja que a conexão com um servidor UPD é diferente, então o cliente UDP também precisa ser específico pra isso. Por isso, não tente usar um cliente TCP para se conectar a um servidor UDP, ou melhor, tente pra ver o que acontece (¬‿¬)
Veja que aqui temos uma troca de mensagens via buffer de bytes.
1package main
2
3import (
4 "bufio"
5 "fmt"
6 "net"
7 "os"
8 "strings"
9)
10
11func main() {
12 arguments := os.Args
13 if len(arguments) == 1 {
14 fmt.Println("Please provide a host:port string")
15 return
16 }
17 CONNECT := arguments[1]
18
19 s, err := net.ResolveUDPAddr("udp4", CONNECT)
20 c, err := net.DialUDP("udp4", nil, s)
21 if err != nil {
22 fmt.Println(err)
23 return
24 }
25
26 fmt.Printf("The UDP server is %s\n", c.RemoteAddr().String())
27 defer c.Close()
28
29 for {
30 reader := bufio.NewReader(os.Stdin)
31 fmt.Print(">> ")
32 text, _ := reader.ReadString('\n')
33 data := []byte(text + "\n")
34 _, err = c.Write(data)
35 if strings.ToUpper(strings.TrimSpace(string(data))) == "EXIT" {
36 fmt.Println("Exiting UDP client!")
37 return
38 }
39
40 if err != nil {
41 fmt.Println(err)
42 return
43 }
44
45 buffer := make([]byte, 1024)
46 n, _, err := c.ReadFromUDP(buffer)
47 if err != nil {
48 fmt.Println(err)
49 return
50 }
51 fmt.Printf("Reply: %s\n", string(buffer[0:n]))
52 }
53}
UDP Servidor
E então, nosso servidor UDP. É muito parecido com o TCP, mas configurado de forma diferente e agora trabalha com mensagens na forma de buffer de bytes.
1package main
2
3import (
4 "fmt"
5 "math/rand"
6 "net"
7 "os"
8 "strconv"
9 "strings"
10 "time"
11)
12
13func random(min, max int) int {
14 return rand.Intn(max-min) + min
15}
16
17func main() {
18 arguments := os.Args
19 if len(arguments) == 1 {
20 fmt.Println("Please provide a port number!")
21 return
22 }
23 PORT := ":" + arguments[1]
24
25 s, err := net.ResolveUDPAddr("udp4", PORT)
26 if err != nil {
27 fmt.Println(err)
28 return
29 }
30
31 connection, err := net.ListenUDP("udp4", s)
32 if err != nil {
33 fmt.Println(err)
34 return
35 }
36
37 defer connection.Close()
38 buffer := make([]byte, 1024)
39 rand.Seed(time.Now().Unix())
40
41 for {
42 /*
43 Veja que estamos trabalhando com leitura em buffer, em bytes.
44 A função ReadFromUDP retorna também o tamanho da mensagem que
45 estamos recebendo no buffer e transformamos em string
46 */
47 n, addr, err := connection.ReadFromUDP(buffer)
48 fmt.Print(addr, " -> ", string(buffer[0:n-1]))
49
50 // Se a mensagem for exit, fechamos a conexão e o servidor
51 if strings.ToUpper(strings.TrimSpace(string(buffer[0:n]))) == "EXIT" {
52 fmt.Println("Exiting UDP server!")
53 return
54 }
55
56 data := []byte(strconv.Itoa(rand.Intn(1000)))
57 fmt.Println(addr, "<-", string(data))
58 _, err = connection.WriteToUDP(data, addr)
59 if err != nil {
60 fmt.Println(err)
61 return
62 }
63 }
64}
Exemplo de Computação Distribuída
Servidor
1package main
2
3import (
4 "bufio"
5 "encoding/json"
6 "fmt"
7 "net"
8 "os"
9 "strconv"
10)
11
12type Message struct {
13 Begin int `json:"begin"`
14 End int `json:"end"`
15}
16
17func child(conn net.Conn) {
18 defer conn.Close()
19 netData, err := bufio.NewReader(conn).ReadString('\n')
20 if err != nil {
21 fmt.Println(err)
22 return
23 }
24 msg := Message{}
25 json.Unmarshal([]byte(netData), &msg)
26 out := 1
27 for i := msg.Begin; i <= msg.End; i++ {
28 out *= i
29 }
30 fmt.Println(msg.Begin, "->", msg.End, ":", out)
31 conn.Write([]byte(strconv.Itoa(out)))
32}
33
34func main() {
35 arguments := os.Args
36 if len(arguments) == 1 {
37 fmt.Println("Enter with port number in argument")
38 return
39 }
40 l, err := net.Listen("tcp", ":"+arguments[1])
41 if err != nil {
42 fmt.Println(err)
43 return
44 }
45 defer l.Close()
46 fmt.Println("TCP Server initialized at port", arguments[1])
47
48 for {
49 c, err := l.Accept()
50 if err != nil {
51 fmt.Println(err)
52 return
53 }
54 go child(c)
55 }
56}
Cliente
1package main
2
3import (
4 "bufio"
5 "encoding/json"
6 "fmt"
7 "net"
8 "os"
9 "strconv"
10 "sync"
11)
12
13type Message struct { // Estrutura da mensagem
14 Begin int `json:"begin"`
15 End int `json:"end"`
16}
17
18func connect_server(wg *sync.WaitGroup, ch chan int, server string, begin, end int) {
19 defer wg.Done()
20 c, err := net.Dial("tcp", server) // Conecta ao servidor
21 if err != nil {
22 fmt.Println(err)
23 return
24 }
25 defer c.Close()
26 msg := Message{begin, end} // cria uma mensagem na estrutura definida
27 msgJson, _ := json.Marshal(msg) // transforma em string json
28 fmt.Fprintf(c, string(msgJson)+"\n") // envia ao servidor
29 message, _ := bufio.NewReader(c).ReadString('\n') // aguarda a resposta
30 result, err := strconv.Atoi(message) // transforma a resposta para inteiro
31 if err == nil {
32 fmt.Println(begin, "->", end, ":", result)
33 ch <- result // manda o resultado para o programa principal
34 }
35}
36
37/*
38 EXECUÇÃO:
39 go run fat_dist_cli.go [value] [rotines] [server:port] ...
40
41 1: Valor para calcular o fatorial
42 2: Quantidade de gorotines, ou conexões a servidores
43 3 em diante: servidores para se conectar no formato servidor:porta
44
45 EXEMPLO:
46
47 go run fat_dist_cli.go 20 5 localhost:4400 localhost:4402 localhost:4401
48
49 O exemplo acima pede para calcular o fatorial de 20, dividindo em 5
50 subrotinas. Em sequência temos 3 servidores para utilizar. Como o número
51 de servidores é menor do que a quantidade de subrotinas, dois deles vão receber
52 duas requisições de cálculos
53*/
54
55func main() {
56 args := os.Args // pega os argumentos
57 value, _ := strconv.Atoi(args[1]) // pega o valor
58 rotines, _ := strconv.Atoi(args[2]) // pega a quantidade de rotinas
59 size := int(value / rotines) // calcula o tamanho do bloco a separar
60 servers := make([]string, len(args)-3) // cria o vetor de servidores
61 j := 0 //
62 for i := 3; i < len(args); i++ { // coloca os parâmetros como servidores
63 servers[j] = args[i]
64 j++
65 }
66
67 var wg sync.WaitGroup
68 wg.Add(rotines)
69 ch := make(chan int, 10)
70
71 j = 0
72 end := 0
73 for i := 0; i < rotines; i++ { // cria as subrotinas
74 begin := end + 1 // separa o inicio
75 end = begin + size // e o fim do bloco de cálculo
76 if end > value { // Não pode passar do valor total
77 end = value
78 }
79 go connect_server(&wg, ch, servers[j], begin, end) // envia para um servidor
80 j++
81 if j >= len(servers) {
82 j = 0
83 }
84 }
85 wg.Wait() // aguarda os servidores terminarem
86 close(ch)
87 // Pega os resultados e multiplica-os
88 result := 1
89 for v := range ch {
90 result = result * v
91 }
92 // apresenta o resultado final
93 fmt.Println("Fatorial de", value, "é", result)
94}