Расскажите о протоколе TCP и приведите примеры его использования в Go

Ответ

TCP (Transmission Control Protocol) — это один из основных протоколов сетевой модели TCP/IP. Его главная задача — обеспечить надежную, упорядоченную и безошибочную доставку потока данных между двумя программами, запущенными на разных хостах в сети.

Ключевые характеристики TCP:

  • Надежность (Reliability): Гарантирует, что данные будут доставлены. Если пакет теряется, TCP запрашивает его повторную отправку.
  • Установка соединения (Connection-oriented): Перед обменом данными клиенту и серверу необходимо установить соединение через процесс "тройного рукопожатия" (three-way handshake: SYN, SYN-ACK, ACK).
  • Упорядоченная доставка (Ordered Delivery): TCP гарантирует, что данные будут получены в том же порядке, в котором были отправлены.
  • Контроль потока (Flow Control): Механизм, который не позволяет быстрому отправителю перегрузить медленного получателя.

Благодаря этим свойствам, TCP используется в большинстве популярных прикладных протоколов, где важна целостность данных: HTTP/HTTPS, FTP, SMTP, SSH, Telnet.

Примеры использования в Go:

В стандартной библиотеке Go (net) есть все необходимое для работы с TCP.

  1. Простой TCP-сервер Сервер слушает порт, принимает входящие соединения и обрабатывает каждое в отдельной горутине.

    package main
    
    import (
        "fmt"
        "net"
    )
    
    func handleConnection(conn net.Conn) {
        defer conn.Close()
        fmt.Printf("Accepted connection from %sn", conn.RemoteAddr())
        // ... логика чтения/записи данных
        conn.Write([]byte("Hello from server!n"))
    }
    
    func main() {
        // Начинаем слушать порт 8080 по протоколу TCP
        ln, err := net.Listen("tcp", ":8080")
        if err != nil {
            panic(err)
        }
        defer ln.Close()
        fmt.Println("Server is listening on port 8080")
    
        for {
            // Ожидаем и принимаем новое соединение
            conn, err := ln.Accept()
            if err != nil {
                fmt.Println("Error accepting connection:", err)
                continue
            }
            // Обрабатываем соединение в новой горутине
            go handleConnection(conn)
        }
    }
  2. TCP-клиент Клиент устанавливает соединение с сервером, отправляет данные и читает ответ.

    package main
    
    import (
        "bufio"
        "fmt"
        "net"
        "time"
    )
    
    func main() {
        // Устанавливаем соединение с таймаутом
        conn, err := net.DialTimeout("tcp", "localhost:8080", 5*time.Second)
        if err != nil {
            panic(err)
        }
        defer conn.Close()
    
        fmt.Println("Connected to server!")
    
        // Отправляем данные
        _, err = conn.Write([]byte("Hello, server!n"))
        if err != nil {
            panic(err)
        }
    
        // Читаем ответ от сервера
        message, _ := bufio.NewReader(conn).ReadString('n')
        fmt.Print("Message from server: " + message)
    }

В отличие от UDP, который является более быстрым, но ненадёжным протоколом без установки соединения, TCP выбирают тогда, когда потеря или нарушение порядка данных недопустимы.