Ключевые отличия TCP и UDP

Какие ключевые отличия TCP и UDP?

Теория: что именно отличает TCP и UDP

TCP предоставляет приложению надёжный байтовый поток «по порядку» и является протоколом с установлением соединения.
UDP предоставляет «датаграммный» режим обмена с минимумом механизмов и без гарантий доставки, защиты от дублей и упорядочивания.
Для веб-разработки это означает: выбор TCP упрощает корректность и целостность данных, а выбор UDP упрощает минимизацию задержки и даёт больше контроля приложению, но повышает ответственность за надёжность.

Часто путаются уровни: IP доставляет «как получится», а свойства «надёжно/в порядке» (если нужны) реализуются на транспортном уровне (TCP) или на прикладном уровне поверх UDP.

Ключевые свойства в таблице

СвойствоTCPUDP
Модель данныхБайтовый поток (границ сообщений нет)Датаграммы (границы сообщений сохраняются)
СоединениеЕсть установление соединения и состоянияСоединения на уровне протокола нет
ДоставкаНадёжная (повторы при потерях)Best-effort (может потеряться)
ПорядокГарантирован для байтового потокаНе гарантирован
ДублиОтфильтровываются механизмами TCPВозможны, требуется дедупликация приложением
Контроль потокаЕсть (защита получателя)Нет (нужно делать в приложении при необходимости)
Контроль перегрузкиЕсть (адаптация скорости)Нет (при необходимости реализуется поверх UDP)
Задержка при потеряхМожет расти из-за переотправок и ожидания порядкаМожет быть ниже, так как можно пропускать потери
Утверждение «UDP быстрее TCP» некорректно как общее правило: при хорошей сети TCP может быть сопоставим или быстрее за счёт оптимизаций, а при потерях UDP может «казаться быстрее» только потому, что данные не восстанавливаются автоматически.

Разбор отличий

Поток vs датаграммы (границы сообщений)

TCP:

  • Передаются байты, а не сообщения.
  • Одна операция записи не равна одному «получению»: данные могут прийти частями или склеенными.

UDP:

  • Одна отправка соответствует одной датаграмме.
  • Если датаграмма пришла, она приходит целиком (в рамках лимитов), и граница сообщения сохраняется.
В TCP почти всегда требуется протоколирование поверх потока: длина + payload, разделители строк, фреймы, сериализация.

Надёжность: подтверждения и повторы

TCP:

  • Используются номера последовательности и подтверждения.
  • Потерянные сегменты переотправляются автоматически.

UDP:

  • Подтверждений и повторов нет.
  • Если требуется надёжность, она проектируется на прикладном уровне: messageId, ack, timeout, retry, окно.

Порядок и «блокировка по порядку» (HOL)

TCP:

  • Приложение получает байты строго по порядку.
  • При потере сегмента последующие байты могут «застрять», пока не будет восстановлена «дыра» (head-of-line blocking для потока).

UDP:

  • Порядок не гарантирован.
  • Потеря одного сообщения не блокирует доставку других; возможно продолжение работы «как есть».

Контроль потока и перегрузки

TCP:

  • Контроль потока предотвращает переполнение буфера получателя.
  • Контроль перегрузки уменьшает скорость при признаках перегрузки сети, повышая устойчивость.

UDP:

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

Накладные расходы и «время до первого байта»

TCP:

  • Требуется установление соединения (рукопожатие), что добавляет задержку перед началом передачи.
  • Состояние соединения позволяет эффективно управлять передачей при длительных сессиях.

UDP:

  • Можно отправлять сразу, без рукопожатия на уровне UDP.
  • Для защищённых или надёжных поверхностей (например, современные транспорты поверх UDP) рукопожатие и криптография появляются уже в протоколах поверх UDP.

Схемы (упрощённо)

TCP: установление соединения (3 шага)

клиент                               сервер
SYN (seq=x)        -----------------> 
                   <-----------------  SYN+ACK (seq=y, ack=x+1)
ACK (ack=y+1)      -----------------> 
(соединение установлено, можно передавать данные)

UDP: обмен без соединения

клиент                               сервер
DATAGRAM #1         -----------------> 
DATAGRAM #2         -----------------> 
(ответ может прийти, может не прийти, порядок не гарантирован)

Где это встречается в вебе

  • HTTP/1.1 и HTTP/2 обычно работают поверх TCP: важна целостность данных, а повторные передачи допустимы.
  • WebSocket работает поверх TCP (через HTTP Upgrade): для приложений удобен надёжный поток, но требуется учитывать, что TCP — это поток без границ сообщений.
  • WebRTC чаще использует UDP: для голоса/видео важнее задержка, чем идеальная доставка каждого пакета.
  • HTTP/3 работает поверх транспорта на базе UDP (через QUIC): достигаются надёжность и мультиплексирование без части ограничений классического TCP на уровне одного потока.
В браузерной разработке прямой выбор TCP/UDP чаще скрыт: выбор делается через протокол/технологию (WebSocket против WebRTC, HTTP/2 против HTTP/3).

Примеры кода (Node.js)

TCP: сервер и клиент (важно: это поток)

TCP сервер:

const net = require('net');

const server = net.createServer((socket) => {
  socket.setNoDelay(true);

  socket.on('data', (chunk) => {
    // chunk — произвольная порция байт: нет границ сообщений
    socket.write(chunk);
  });

  socket.on('error', () => {});
});

server.listen(9000, '127.0.0.1');

TCP клиент:

const net = require('net');

const socket = net.connect({ host: '127.0.0.1', port: 9000 }, () => {
  socket.write('hello');
  socket.write('world');
});

socket.on('data', (chunk) => {
  // может прийти 'helloworld', или 'hello' затем 'world', или иначе
});
Для TCP нельзя полагаться на «одно отправление = одно получение», поэтому для сообщений следует добавлять фрейминг.

Пример фрейминга «длина (uint32be) + payload»:

const net = require('net');

function encodeFrame(buf) {
  const header = Buffer.alloc(4);
  header.writeUInt32BE(buf.length, 0);
  return Buffer.concat([header, buf]);
}

function createFrameDecoder(onFrame) {
  let acc = Buffer.alloc(0);

  return (chunk) => {
    acc = Buffer.concat([acc, chunk]);

    while (acc.length >= 4) {
      const len = acc.readUInt32BE(0);
      if (acc.length < 4 + len) break;

      const frame = acc.slice(4, 4 + len);
      acc = acc.slice(4 + len);
      onFrame(frame);
    }
  };
}

const server = net.createServer((socket) => {
  const decode = createFrameDecoder((frame) => {
    socket.write(encodeFrame(frame));
  });

  socket.on('data', decode);
});

server.listen(9001, '127.0.0.1');

UDP: сервер и клиент (важно: это датаграммы)

UDP сервер:

const dgram = require('dgram');
const server = dgram.createSocket('udp4');

server.on('message', (msg, rinfo) => {
  // msg — одна датаграмма, границы сообщения сохранены
  server.send(msg, rinfo.port, rinfo.address);
});

server.bind(9002, '127.0.0.1');

UDP клиент:

const dgram = require('dgram');
const client = dgram.createSocket('udp4');

client.send(Buffer.from('ping'), 9002, '127.0.0.1');

client.on('message', (msg) => {
  // доставка не гарантирована; может не прийти
});
Если поверх UDP требуется «как TCP, но проще», это обычно приводит к частичной реализации TCP заново; следует заранее определить требования (допустимые потери, порядок, дедлайны, размер окна).

Команды для быстрой проверки

Полезные однострочные примеры:

  • Проверка TCP порта: nc -vz 127.0.0.1 9000
  • Отправка UDP датаграммы: echo -n "ping" | nc -u -w1 127.0.0.1 9002

Кратко: TCP — это надёжный упорядоченный байтовый поток с установлением соединения, подтверждениями, повторами и встроенными механизмами контроля потока и перегрузки, что удобно для большинства веб-протоколов. UDP — это обмен датаграммами без соединения и без гарантий доставки/порядка, что снижает накладные расходы и может уменьшать задержку, но переносит ответственность за надёжность и управление скоростью на прикладной уровень, что особенно важно для real-time сценариев (например, WebRTC) и современных транспортов поверх UDP (например, HTTP/3 через QUIC).