Сетевое программирование

8. Сетевое программирование #

Сетевое программирование в Go строится на богатой стандартной библиотеке и поддерживает различные протоколы, такие как TCP, UDP, HTTP и современные технологии вроде gRPC и WebSocket.


8.1 Основы работы с TCP и UDP #

TCP (Transmission Control Protocol) #

Для работы с TCP используются функции пакета net.

Сервер TCP:

package main

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

func handleConnection(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    for {
        message, _ := reader.ReadString('\n')
        fmt.Print("Сообщение от клиента: ", message)
        if strings.TrimSpace(message) == "exit" {
            fmt.Println("Клиент отключился.")
            break
        }
        conn.Write([]byte("Принято: " + message))
    }
}

func main() {
    listener, _ := net.Listen("tcp", ":8080")
    defer listener.Close()
    fmt.Println("TCP сервер запущен на порту 8080")

    for {
        conn, _ := listener.Accept()
        go handleConnection(conn)
    }
}

Клиент TCP:

package main

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

func main() {
    conn, _ := net.Dial("tcp", "localhost:8080")
    defer conn.Close()

    for {
        fmt.Print("Введите сообщение: ")
        input := bufio.NewReader(os.Stdin)
        message, _ := input.ReadString('\n')
        conn.Write([]byte(message))

        reply, _ := bufio.NewReader(conn).ReadString('\n')
        fmt.Print("Ответ сервера: ", reply)
    }
}

UDP (User Datagram Protocol) #

Сервер UDP:

package main

import (
    "fmt"
    "net"
)

func main() {
    addr, _ := net.ResolveUDPAddr("udp", ":8080")
    conn, _ := net.ListenUDP("udp", addr)
    defer conn.Close()
    fmt.Println("UDP сервер запущен на порту 8080")

    buffer := make([]byte, 1024)
    for {
        n, clientAddr, _ := conn.ReadFromUDP(buffer)
        fmt.Printf("Сообщение от %s: %s\n", clientAddr, string(buffer[:n]))
        conn.WriteToUDP([]byte("Принято\n"), clientAddr)
    }
}

Клиент UDP:

package main

import (
    "fmt"
    "net"
)

func main() {
    serverAddr, _ := net.ResolveUDPAddr("udp", "localhost:8080")
    conn, _ := net.DialUDP("udp", nil, serverAddr)
    defer conn.Close()

    message := []byte("Привет UDP!")
    conn.Write(message)

    buffer := make([]byte, 1024)
    n, _, _ := conn.ReadFromUDP(buffer)
    fmt.Println("Ответ сервера:", string(buffer[:n]))
}

8.2 HTTP-серверы и клиенты #

Для работы с HTTP используется пакет net/http.

HTTP-сервер: #

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Привет, Go HTTP!")
}

func main() {
    http.HandleFunc("/", handler)
    fmt.Println("HTTP сервер запущен на :8080")
    http.ListenAndServe(":8080", nil)
}

HTTP-клиент: #

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    resp, _ := http.Get("http://example.com")
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

8.3 gRPC: основы и примеры #

Для работы с gRPC используется пакет google.golang.org/grpc.

Шаги работы с gRPC: #

  1. Создайте .proto файл для описания сервиса.
  2. Сгенерируйте сервер и клиент с помощью protoc.
  3. Реализуйте сервер на Go.

Пример service.proto:

syntax = "proto3";

package example;

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
    string name = 1;
}

message HelloResponse {
    string message = 1;
}

Сервер gRPC:

package main

import (
    "context"
    "fmt"
    "net"

    pb "example/proto" // Сгенерированный код
    "google.golang.org/grpc"
)

type server struct {
    pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
    return &pb.HelloResponse{Message: "Привет, " + req.Name}, nil
}

func main() {
    listener, _ := net.Listen("tcp", ":50051")
    grpcServer := grpc.NewServer()
    pb.RegisterGreeterServer(grpcServer, &server{})
    fmt.Println("gRPC сервер запущен на :50051")
    grpcServer.Serve(listener)
}

Клиент gRPC:

package main

import (
    "context"
    "fmt"
    "time"

    pb "example/proto"
    "google.golang.org/grpc"
)

func main() {
    conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
    defer conn.Close()

    client := pb.NewGreeterClient(conn)
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()

    res, _ := client.SayHello(ctx, &pb.HelloRequest{Name: "Мир"})
    fmt.Println(res.Message)
}

8.4 Вебсокеты #

Для работы с WebSocket используется сторонний пакет, например, github.com/gorilla/websocket.

Сервер WebSocket:

package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{}

func wsHandler(w http.ResponseWriter, r *http.Request) {
    conn, _ := upgrader.Upgrade(w, r, nil)
    defer conn.Close()

    for {
        _, message, err := conn.ReadMessage()
        if err != nil {
            break
        }
        fmt.Println("Получено:", string(message))
        conn.WriteMessage(websocket.TextMessage, []byte("Ответ: "+string(message)))
    }
}

func main() {
    http.HandleFunc("/ws", wsHandler)
    fmt.Println("WebSocket сервер запущен на :8080/ws")
    http.ListenAndServe(":8080", nil)
}

Клиент WebSocket:

package main

import (
    "fmt"
    "github.com/gorilla/websocket"
)

func main() {
    conn, _, _ := websocket.DefaultDialer.Dial("ws://localhost:8080/ws", nil)
    defer conn.Close()

    conn.WriteMessage(websocket.TextMessage, []byte("Привет, WebSocket!"))
    _, message, _ := conn.ReadMessage()
    fmt.Println("Ответ:", string(message))
}

8.5 Работа с REST API #

Отправка GET-запроса:

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

type Post struct {
    UserID int    `json:"userId"`
    ID     int    `json:"id"`
    Title  string `json:"title"`
    Body   string `json:"body"`
}

func main() {
    resp, _ := http.Get("https://jsonplaceholder.typicode.com/posts/1")
    defer resp.Body.Close()

    var post Post
    json.NewDecoder(resp.Body).Decode(&post)
    fmt.Println(post)
}

Отправка POST-запроса:

package main

import (
    "bytes"
    "fmt"
    "net/http"
)

func main() {
    jsonData := []byte(`{"title": "foo", "body": "bar", "userId": 1}`)
    resp, _ := http.Post("https://jsonplaceholder.typicode.com/posts", "application/json", bytes.NewBuffer(jsonData))
    defer resp.Body.Close()

    fmt.Println("Ответ сервера:", resp.Status)
}

Эти примеры покрывают основы сетевого программирования в Go, от низкоуровневого TCP до высокоуровневых HTTP и gRPC.