Ответ
Процесс можно разделить на два уровня: сетевой (TCP) и прикладной (ваше Go-приложение).
1. Уровень TCP (происходит автоматически, прозрачно для приложения):
- Таймаут и повторная передача (Retransmission): Отправитель запускает таймер (RTO, Retransmission Timeout) для каждого отправленного пакета. Если подтверждение (ACK) не приходит до истечения таймера, TCP считает пакет утерянным и отправляет его заново.
- Экспоненциальное отступление (Exponential Backoff): При каждой последующей неудачной попытке время ожидания RTO удваивается. Это помогает избежать перегрузки сети.
- Разрыв соединения: Если после множества попыток (например, 10-15 раз, зависит от ОС) подтверждение так и не получено, TCP-стек считает соединение разорванным и уведомляет об этом приложение.
2. Уровень приложения в Go:
Для приложения все эти сетевые сложности выглядят как простая задержка. Однако если TCP в итоге сдаётся, приложение получает ошибку. Чаще всего это ошибка таймаута.
В Go для контроля этого процесса используются дедлайны:
// Устанавливаем общий дедлайн на 5 секунд для всех последующих операций
conn, err := net.Dial("tcp", "example.com:80")
if err != nil {
log.Fatal("Connection failed:", err)
}
defer conn.Close()
// Устанавливаем дедлайн на операцию записи
err = conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
if err != nil {
// Обработка ошибки установки дедлайна
}
_, err = conn.Write([]byte("GET / HTTP/1.1rnHost: example.comrnrn"))
if err != nil {
// Если ответ не придет вовремя, здесь будет ошибка,
// скорее всего net.OpError с сообщением о таймауте (i/o timeout)
log.Println("Write failed:", err)
}
Итог: Приложение не управляет переотправкой пакетов, но должно быть готово к обработке ошибок таймаута или разрыва соединения, которые являются следствием проблем на уровне TCP.