MySQL Java JavaScript PHP Python HTML-CSS C-sharp C++ Go

Interacțiunea dintre client și server

În tema precedentă am analizat crearea unui server TCP simplu, la care se conectează un client și căruia i se trimite un mesaj. Acum vom vedea cum clientul poate trimite mesaje către server și primi răspunsuri înapoi.

Pentru început, vom defini următorul server:

package main
import (
    "fmt"
    "net"
)

var dict = map[string]string{ 
    "red": "roșu",
    "green": "verde",
    "blue": "albastru",
    "yellow": "galben",
}

func main() {
    listener, err := net.Listen("tcp", ":4545") 
    
    if err != nil {
        fmt.Println(err) 
        return
    } 
    defer listener.Close() 
    fmt.Println("Server is listening...")
    for { 
        conn, err := listener.Accept() 
        if err != nil { 
            fmt.Println(err) 
            conn.Close() 
            continue
        } 
        go handleConnection(conn)  // pornim o gorutină pentru a procesa cererea
    } 
}

// procesarea conexiunii
func handleConnection(conn net.Conn) { 
    defer conn.Close()
    for {
        // citim datele primite în cerere
        input := make([]byte, (1024 * 4)) 
        n, err := conn.Read(input) 
        if n == 0 || err != nil {
            fmt.Println("Read error:", err)
            break
        }    
        source := string(input[0:n])
        // pe baza datelor primite, obținem traducerea din dicționar 
        target, ok := dict[source]
        if ok == false {             // dacă datele nu se găsesc în dicționar
            target = "undefined"
        }
        // afișăm în consola serverului informații de diagnostic
        fmt.Println(source, "-", target)
        // trimitem datele clientului
        conn.Write([]byte(target))
    }
}

Serverul imită comportamentul unei aplicații de traducere a cuvintelor. Pentru aceasta este definit un dicționar dict, care conține cuvinte în limba engleză și traducerea lor.

Într-o buclă infinită, serverul acceptă conexiuni. Însă, în loc să proceseze conexiunea direct, serverul lansează o gorutină prin funcția handleConnection, unde are loc procesarea conexiunii. Acest lucru permite ca noii clienți să nu fie nevoiți să aștepte până când primul este procesat. Astfel, toți clienții care se conectează sunt gestionați într-un anumit grad simultan.

În funcția handleConnection, serverul primește cererea de la client. Pentru aceasta se alocă un buffer cu o lungime suficientă – 4096 de octeți.

	
input := make([]byte, (1024 * 4)) 
n, err := conn.Read(input) 

În acest caz, presupunem că cererea trimisă de client nu va depăși 4096 de octeți. Totuși, dimensiunea exactă a cererii și limita maximă nu sunt întotdeauna cunoscute. În astfel de situații, se pot folosi diverse tehnici — de exemplu, citirea datelor în buclă până la finalul transmisiei și abia apoi procesarea lor. Dar în acest exemplu analizăm un caz mai simplu.

După ce primim cererea și o convertim într-un șir de caractere, extragem valoarea corespunzătoare din dicționar și o trimitem înapoi clientului.

conn.Write([]byte(target))

Pentru a interacționa cu acest server, vom defini următorul client:

package main
import (
    "fmt"
    "net"
)

func main() {

    conn, err := net.Dial("tcp", "127.0.0.1:4545") 
    if err != nil { 
        fmt.Println(err) 
        return
    }
    defer conn.Close()
    
    for {
        var source string
        fmt.Print("Introduceți un cuvânt: ") 
        _, err := fmt.Scanln(&source) 
        if err != nil { 
            fmt.Println("Intrare incorectă:", err) 
            continue
        }
        // trimitem mesajul către server
        if n, err := conn.Write([]byte(source)); n == 0 || err != nil { 
            fmt.Println(err) 
            return
        }
        // primim răspunsul
        fmt.Print("Traducere: ")
        buff := make([]byte, 1024)
        n, err := conn.Read(buff)
        if err != nil {
            break
        }
        fmt.Print(string(buff[0:n]))
        fmt.Println()
    }
}

На клиенте в бесконечном цикле вводим слово для перевода и отправляем серверу сообщение:

if n, err := conn.Write([]byte(source));

Apoi primim răspunsul de la server și îl afișăm în consolă. Deoarece răspunsul de la server poate avea o lungime variabilă, pentru a-l citi folosim o buclă infinită în care citim datele cu ajutorul metodei Read:

buff := make([]byte, 1024)
n, err := conn.Read(buff)
if err !=nil{ break}
fmt.Print(string(buff[0:n]))

Să pornim serverul.

După care rulăm serverul și clientul și încercăm să interacționăm. Dacă nu reușiți, nu ezitați să întrebați pe canalul nostru de Discord!