> Есть ли у TCP и UDP операция подтверждения доставки (Go)
Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: Avito
Стек: Go
> Пример ответа
Короткий ответ
У TCP есть встроенное подтверждение доставки - механизм ACK (acknowledgment), который работает на уровне протокола и гарантирует доставку сегментов. UDP не имеет никакого подтверждения доставки - это fire-and-forget протокол. Если нужна гарантия доставки поверх UDP, её реализуют на прикладном уровне (например, QUIC, WebRTC, или собственный протокол с ACK). В Go это различие проявляется при использовании net.TCPConn и net.UDPConn.
Подробное объяснение
TCP - это протокол с установлением соединения и гарантированной доставкой. Подтверждение доставки реализовано через:
- ACK-сегменты: получатель отправляет отправителю подтверждение о получении данных. Номер ACK указывает на следующий ожидаемый байт.
- Кумулятивные ACK: одно подтверждение может покрывать несколько сегментов.
- Таймауты и ретрансмиссия: если отправитель не получает ACK за RTO (retransmission timeout), он повторно отправляет данные.
- SACK (Selective Acknowledgment): позволяет подтверждать отдельные сегменты, а не только кумулятивный поток.
UDP - это протокол без установления соединения. Он просто отправляет датаграммы в сеть без какой-либо обратной связи. Никаких ACK, никаких таймаутов, никакой ретрансмиссии. Причины:
- Минимальный overhead - заголовок 8 байт против 20+ у TCP.
- Низкая задержка - нет handshake, нет ожидания подтверждений.
- Подходит для real-time приложений (VoIP, стриминг, игры), где потеря пакета лучше, чем задержка.
Важно понимать: даже в TCP подтверждение доставки не означает, что приложение получило данные - это подтверждение на уровне ядра ОС. Гарантия доставки заканчивается на уровне сетевого стека получателя.
На практике
В Go работа с подтверждениями доставки зависит от выбранного протокола:
-
TCP: используете
net.Dial("tcp", ...)- подтверждения обрабатываются автоматически. Приложение получает гарантию, что данные записаны в сокет, но не что они прочитаны на другом конце. Для подтверждения на уровне приложения нужно реализовать собственный протокол (например, отправить ответное сообщение). -
UDP:
net.Dial("udp", ...)илиnet.ListenPacket("udp", ...)- нет никаких подтверждений. Если нужна надёжность, вы реализуете её сами: добавляете sequence numbers, таймауты, ретрансмиссию, ACK-сообщения. Это сложно сделать правильно - именно поэтому появился QUIC, который работает поверх UDP, но предоставляет надёжность TCP. -
QUIC: в Go можно использовать
github.com/quic-go/quic-go- это протокол поверх UDP с подтверждениями, мультиплексированием и меньшей задержкой, чем TCP.
Пример кода
GO// TCP: подтверждение доставки на уровне протоколаconn, err := net.Dial("tcp", "example.com:80")if err != nil {log.Fatal(err)}defer conn.Close()_, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"))if err != nil {log.Fatal(err)}// TCP ACK уже отправлен и обработан ядром.// Но мы не знаем, получил ли сервер данные, пока не прочитаем ответ.buf := make([]byte, 1024)n, err := conn.Read(buf)if err != nil {log.Fatal(err)}fmt.Printf("Ответ сервера: %s\n", buf[:n])
GO// UDP: нет подтверждений, нужно реализовывать самомуconn, err := net.Dial("udp", "example.com:1234")if err != nil {log.Fatal(err)}defer conn.Close()_, err = conn.Write([]byte("ping"))if err != nil {log.Fatal(err)}// Никакой гарантии, что пакет дошёл.// Если нужен ACK, нужно ждать ответного пакета с таймаутом.conn.SetReadDeadline(time.Now().Add(2 * time.Second))buf := make([]byte, 1024)n, err := conn.Read(buf)if err != nil {log.Println("Нет подтверждения:", err)return}fmt.Printf("Получен ACK: %s\n", buf[:n])
Как отвечать на собеседовании
Начните с прямого ответа: TCP - да, UDP - нет. Затем кратко объясните механизм ACK в TCP. Упомяните, что подтверждение в TCP работает на уровне протокола и не гарантирует доставку приложению - это важный нюанс. Для UDP скажите, что надёжность можно добавить на прикладном уровне, и приведите примеры: QUIC, собственные протоколы. Если спросят про Go - покажите разницу между net.TCPConn и net.UDPConn, упомяните, что в TCP всё автоматически, а в UDP нужно писать свою логику подтверждений. Не углубляйтесь в детали реализации TCP-стека, если не просят - достаточно упомянуть ACK, таймауты и ретрансмиссию.
Что проверяет интервьюер
Интервьюер проверяет:
- Понимание фундаментальной разницы между TCP и UDP - это база, которую должен знать senior.
- Умение объяснить, что подтверждение доставки - это не то же самое, что гарантия доставки приложению.
- Понимание trade-off: надёжность TCP против скорости и простоты UDP.
- Знание современных решений поверх UDP (QUIC) - это плюс для senior.
- Умение применить знания в контексте Go: как это выглядит на практике с
netпакетом.
Типичные ошибки
- Утверждение, что TCP гарантирует доставку данных приложению - это не так, гарантия заканчивается на уровне ядра.
- Забывают упомянуть, что UDP тоже может иметь подтверждения, если их реализовать на прикладном уровне.
- Путают ACK на уровне TCP с подтверждением на уровне приложения - это разные вещи.
- Не упоминают QUIC как современное решение, которое сочетает преимущества UDP и надёжность TCP.
- В контексте Go говорят о подтверждениях в UDP, не уточняя, что это самописная логика, а не встроенная возможность.
> Похожие задачи по Go
Как генерировать короткую ссылку в системе url shortener
На какие действия можно повесить триггер
Что происходит если памяти не хватает
Что происходит с персональными данными при обработке
> Похожие задачи по backend
Как генерировать короткую ссылку в системе url shortener
На какие действия можно повесить триггер
Что происходит если памяти не хватает
Что происходит с персональными данными при обработке
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью