View on GitHub

Methodics of Linux Network Protocols

Practical materials for learning TCP/IP and its implementation on Linux

Общая теория

В предыдущих главах были описаны уровни стека протоколов, обеспечивающие связность самой сети, доступность абонентов в ней и т.д. Транспортный уровень, которому посвящена данная глава, направлен непосредственно на контроль и управление передачей данных.

В рамках транспортного уровня Bottom-half целью является обеспечение цельности передаваемых данных и надёжности передачи. При работе с данными необходимо контролировать, что все отправленные данные достигли получателя. В случае множества пакетов необходимо также следить за их порядком. Для обеспечения надёжности передачи производится управление контролем передачи. Получатель и отправитель организовывают двустороннее соединение, проверяют доступность обеих сторон. При передаче данных в качестве обратной связи передаются данные о состоянии передаваемых пакетов (подтверждение доставки, ошибки, …), а также о состоянии канала для корректировки скорости и / или интенсивности передачи.

Однако, из всего многообразия требований этой цели есть важное исключение: в случае работы не со множеством связанных пакетов, а с одним пакетом (полностью описывающим всю передаваемую информацию) весь жёсткий контроль и управление передачей практически пропадают. Для одного пакета сама его доставка уже гарантирует целостность передачи. Управления контролем передачи также нет: отдельного соединения получателя и отправителя не требуется, относительно передачи сообщения и состоянии канала (а эти данные нужны для корректировки последующей передачи) вопрос даже не поднимается по причине отсутствия последующей передачи как таковой.

В связи с этим весь пласт передаваемых данных разбивается на две группы:

Top-half целью уровня стоит различие потоков и управление ими. В рамках этой задачи необходимо идентифицировать не просто абонентов в сети, а также раздельные потоки (или датаграммы) с одного устройства, поскольку они могут быть логически связаны с разными прикладными протоколами верхнего уровня. Для этого кроме привычных идентификаторов - адреса отправителя и адреса получателя - к соответствующих передаваемых объектам (потокам или датаграммам) добавляется ещё два: порт отправителя и порт получателя. Это позволяет отслеживать параллельно транслирующиеся в сеть данные от разных приложений с одного устройства.

При работе с транспортными протоколами заметна их ассиметричная природа, в связи с чем возникают понятия “клиента” и “сервера”. В случае работы с потоком данных передача, как уже было отмечено, вообще говоря, двунаправленная. Тем не менее, для обозначения взаимодействующих абонентов принято “клиентом” называть инициирующую передачу данных сторону, а “сервером” - принимающую эти данные сторону.

В главе подробно будут рассмотрены два основных протокола передачи данных транспортного уровня: TCP для потока данных и UDP для датаграмм. Основные различия этих протоколов в описанных выше требованиях указаны в таблице:

TCP vs. UDP
Да Подключение Нет
Да Подтверждения Нет
Да Отслеживание цельности потока Нет
Да Отслеживание качества потока Нет
Несколько Количество пакетов Один

TCP

Transmission Control Protocol описывает правила работы с потоками данных. Его основными задачами являются:

Для идентификации соединения используется выше описанный набор из двух адресов и двух портов взаимодействующих устройств. Некоторые порты явно закреплены за существующими сервисами, для Linux их описание есть в файле /etc/services

~/papillon_rouge: cat /etc/services | grep -E ' ((80/)|(443/))'
http               80/tcp
http               80/udp
www                80/tcp
www                80/udp
www-http           80/tcp
www-http           80/udp
http               80/sctp
https             443/tcp
https             443/udp
https             443/sctp

Sequential number

При работе с потоком данных необходимо, во-первых, уметь в случае переупорядочивания передаваемых пакетов восстановить их последовательность, а, во-вторых, при потере или удвоении пакетов определять это (т.е. поддерживать целостность данных). Для этих задач в каждом пакете передаётся специальное число Sequential Number(SEQN), изначально случайно генерирующееся у клиента, и после увеличивающееся в каждом пакете на объём уже переданных данных. Таким образом, несоответствие объёму полученных данных и текущему SEQN (логично, за вычетом начального значения) определяет потерю данных при передаче, а нарушение последовательности SEQN (очевидно, возрастающей) говорит о необходимости переупорядочивания пакетов.

Подключение

Для управления соединением в пакетах передаются служебные данные - флаги. При установлении двунаправленного соединения используется технология трёх рукопожатий (3-way handshake) (Сокращения далее: флаг SYN — запрос на подключение + передача начального SEQN, флаг FIN — запрос на отключение, флаг ACK — подтверждение получения пакета; PAYLOAD - информационная нагрузка пакета):

Сейчас данная простая структура обросла множеством технологий, оптимизирующих передачу. Однако её основа прослеживается везде.

Обеспечение надёжности соединения. Sliding window protocol

В рамках работы с TCP обязательным является подтверждение каждого передаваемого пакета. Следовательно, без оптимизации этого процесса передача данных увеличивается, минимум, вдвое (без учёта потери пакетов, переотправок и т.д.). Для ускорения процесса передачи используется технология “скользящего окна”, позволяющая передавать какое-то количество пакетов без подтверждения. Подробнее разберём данный протокол.

Окном называется последовательная группа пакетов из потока, которую можно передавать или получать без подтверждения каждого. Вся группа отправляется получателю, тот отвечает на каждый пакет своим ack-пакетом. При получении очередного ack-пакета окно «сдвигается» на следующий ещё не полученный. Если пакеты идут с разной скоростью или пропадают, в окне получателя образуется «недопринятая» область:

При отправлении TCP дожидается, пока каждому отправленному пакету придёт подтверждение (годится и подтверждение о приёме одного из следующих пакетов, поскольку согласно протоколу это гарантирует подтверждение всех предыдущих пакетов). Если некоторые подтверждения по тем или иным причинам не пришли, образуется «недопереданная» область.

На логике работы окна построены алгоритмы действий для получателя и отправителя. Для более подробного изучения протокола, а также самостоятельного тестирования его работы рекомендуется прочитать статью на Хабре, попробовать исследовать тестовое приложение, изучить видеоматериал по работе алгоритмов.

Фиксированное окно

Классический алгоритм предполагает, что размеры окон получателя и отправителя фиксированы. Будем называть «очередными» пакеты, принадлежащие уже полученному потоку, а «внеочередными» — пакеты, принадлежащие недопринятой области окна, т. е. полученные раньше, чем какой-то предыдущий пакет потока.

Отправитель:

  1. Передаёт все непереданные пакеты в окне
  2. Параллельно ждёт получения подтверждения каждому из пакетов
    • Подтверждение пакета N означает также подтверждение всех пакетов N-k
  3. При получении подтверждения пакета N начало окна перемещается на N+1 (появляются ещё непереданные пакеты)
  4. Для каждого ещё неподтверждённого пакета отслеживается тайм-аут. По истечении тайм-аута пакет посылается повторно.
  5. При получении диагностики «перешли пакет N» пакет посылается повторно

Получатель:

  1. Ждет получения пакетов в окне
  2. При получении пакета:
  3. Если в окне перед полученным пакетом есть непринятый — получен внеочередной пакет:
    • Посылается (повторно) подтверждение о получении последнего очередного пакета
    • …то есть если последний последовательно принятый пакет — N, а получен внеочередной пакет N+2, посылается подтверждение именно пакета N
  4. Если получен первый пакет в окне (он же — первый непринятый), это очередной пакет:
    • Высчитываются новые границы окна: окно начинается с первого непринятого пакета, то есть сдвигается вперёд на количество принятых. Их может быть более одного: если, скажем, пакеты N+1 и N+2 получены вне очереди, а затем только получен пакет N, и сразу три пакета переходят в категорию принятых.
    • Изменяется указатель последнего очередного пакета (в примере выше — на N+2)
    • Посылается подтверждение о получении последнего очередного пакета (в примере выше — сразу N+2-го)
  5. Повторно полученные пакеты игнорируются.
  6. Если с пакетом что-то не так (не совпадает контрольная сумма и т. п.), отсылается просьба повторной пересылки

Этот алгоритм содержит несколько избыточное количество подтверждений, однако это результат очень простой логики «пакет → подтверждение/ошибка», которая позволяет терять (некоторые) подтверждения без влияния на поток.

Масштабируемое окно

В действительности, окно может быть не только фиксированного размера, но и масштабироваться в зависимости от нагрузки сети, способностей абонентов обрабатывать поток и т.д. Очевидно, что чем больше окно, тем больше потенциальная пропускная способность, но если какое-то устройство на пути передачи данных не готово или не способно передавать большой объём трафика, оно будет всегда терять пакеты из большого окна, что приведёт в посылке ещё большего количества пакетов (ошибки и повторная передача) и будет только усугублять ситуацию. Поэтому размер окна может изменяться в зависимости от «качества» канала передачи данных.

На отправителе (т. н. Congestion window)

  1. Договаривается с получателем о максимальном и минимальном размерах окна
  2. Начинает с минимального размера
  3. При каждом сдвиге окна:
    • Если все пакеты в области сдвига были отосланы с первого раза (без повторов по тайм-ауту или ошибке), окно не только сдвигается, но и увеличивается (например, в два раза, но не больше чем максимальный размер)
    • Если со времени последнего сдвига окна были повторные передачи пакетов, или получен анонс уменьшения размера окна, окно уменьшается до минимального
      • Если получен анонс нулевого размера окна, передача дополнительно приостанавливается на некоторый тайм-аут или до получения другого анонса (что случится раньше)

На получателе (т. н. Receive window)

  1. Если получатель не справляется с обработкой, он анонсирует меньший максимальный размер окна
  2. Если получатель совсем не справляется с обработкой, он анонсирует нулевой размер окна

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

UDP

User Datagram Protocol описывает правила работы с датаграммами. Поскольку передача в данном виде соединения ведётся в виде единичного пакета небольшого размера, не требуется установки соединения, а также отсутствует подтверждение о получении пакета. Если же оно необходимо для отправителя, то контроль его получения ложится на прикладной уровень протоколов. Такой способ организации соединения упрощает взаимодействие запрос-ответных сервисов, порождая не 7-9 пакетов в обе стороны, а ровно по одному от клиента и от сервера.

На UDP работает большинство прикладных протоколов, работающих на запросах, например, DNS, DHCP, NTP.

Сетевые подключения

TCP-соединение предоставляет поток данных, так что все сетевые приложения написаны примерно одинаково:

«Клиентские»

«Серверные»

С точки зрения ОС двунаправленный поток данных (как сложный, типа TCP-соединения, так и более простой, например, между двум процессами или вырожденный, типа датаграмм UDP) — это сокет. Универсальные инструменты по работе с сокетами умеют в оба сценария — и клиентский и серверный, они просто перенаправляют поток данных откуда-то куда-то, наподобие утилиты cat. Содержимое потока (прикладной уровень) их не волнует, зато, как видно из описания выше, даже в самых простых случаях есть множество вариантов (например, по UDP доставлять только одну датаграмму или все, и что такое «все»).

Для работы с сетевым подключением через TCP и UDP можно воспользоваться утилитами netcatи socat.

netcat

netcat - утилита для работы с текстовыми данными в сети. netcat может работать как в роли клиента, отправляя запросы, так и в роли сервера, получая некоторую информацию.

Разберём работу netcat на примере:

athena:
       eth1: intnet
boney:
       eth1: intnet

TCP-соединение с помощью netcat

На локальных машинах 22й порт закреплён за сервисом SSH

athena

[root@athena ~]# ss -lntp
State       Recv-Q      Send-Q             Local Address:Port             Peer Address:Port      Process
LISTEN      0           128                      0.0.0.0:22                    0.0.0.0:*          users:(("sshd",pid=637,fd=3))
LISTEN      0           128                         [::]:22                       [::]:*          users:(("sshd",pid=637,fd=4))
[root@athena ~]#

Поднимем интерфейсы на машинах для получения LLA и обратимся с boney на athena:

athena

[root@athena ~]# ip link set eth1 up
[root@athena ~]# ip a show eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:15:1e:1c brd ff:ff:ff:ff:ff:ff
    altname enp0s8
    altname enx080027151e1c
    inet6 fe80::a00:27ff:fe15:1e1c/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
[root@athena ~]#

boney

[root@boney ~]# ip a show eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:0a:32:40 brd ff:ff:ff:ff:ff:ff
    altname enp0s8
    altname enx0800270a3240
    inet6 fe80::a00:27ff:fe0a:3240/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
[root@boney ~]#

athena

[root@athena ~]# tcpdump -vvi eth1 tcp
tcpdump: listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes

boney

[root@boney ~]# netcat fe80::a00:27ff:fe15:1e1c%eth1 22
SSH-2.0-OpenSSH_9.6
exit
Invalid SSH identification string.
[root@boney ~]#

athena

[root@athena ~]# tcpdump -vvi eth1 tcp
tcpdump: listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes

# Установка соединения

11:45:47.086847 IP6 (flowlabel 0x202b7, hlim 64, next-header TCP (6) payload length: 40) fe80::a00:27ff:fe0a:3240.33014 > athena.ssh:
 Flags [S], cksum 0x90e4 (correct), seq 175653024, win 64800, options [mss 1440,sackOK,TS val 849087953 ecr 0,nop,wscale 7], length 0
11:45:47.086926 IP6 (flowlabel 0x9c15b, hlim 64, next-header TCP (6) payload length: 40) athena.ssh > fe80::a00:27ff:fe0a:3240.33014:
 Flags [S.], cksum 0xadab (incorrect -> 0xb7db), seq 2571304909, ack 175653025, win 64260, options [mss 1440,sackOK,TS val 1994640159
 ecr 849087953,nop,wscale 7], length 0
11:45:47.087198 IP6 (flowlabel 0x202b7, hlim 64, next-header TCP (6) payload length: 32) fe80::a00:27ff:fe0a:3240.33014 > athena.ssh:
 Flags [.], cksum 0xdf9c (correct), seq 1, ack 1, win 507, options [nop,nop,TS val 849087954 ecr 1994640159], length 0

# Передача сообщения-запроса и получение ответа

11:45:47.101576 IP6 (flowlabel 0x9c15b, hlim 64, next-header TCP (6) payload length: 53) athena.ssh > fe80::a00:27ff:fe0a:3240.33014:
 Flags [P.], cksum 0xadb8 (incorrect -> 0x17cc), seq 1:22, ack 1, win 503, options [nop,nop,TS val 1994640174 ecr 849087954], length
21: SSH: SSH-2.0-OpenSSH_9.6
11:45:47.101766 IP6 (flowlabel 0x202b7, hlim 64, next-header TCP (6) payload length: 32) fe80::a00:27ff:fe0a:3240.33014 > athena.ssh:
 Flags [.], cksum 0xdf6a (correct), seq 1, ack 22, win 507, options [nop,nop,TS val 849087968 ecr 1994640174], length 0
11:45:51.550441 IP6 (flowlabel 0x202b7, hlim 64, next-header TCP (6) payload length: 37) fe80::a00:27ff:fe0a:3240.33014 > athena.ssh:
 Flags [P.], cksum 0xf50f (correct), seq 1:6, ack 22, win 507, options [nop,nop,TS val 849092417 ecr 1994640174], length 5
11:45:51.550441 IP6 (flowlabel 0x9c15b, hlim 64, next-header TCP (6) payload length: 32) athena.ssh > fe80::a00:27ff:fe0a:3240.33014:
 Flags [.], cksum 0xada3 (incorrect -> 0xbca7), seq 22, ack 6, win 503, options [nop,nop,TS val 1994644623 ecr 849092417], length 0
11:45:51.550441 IP6 (flowlabel 0x9c15b, hlim 64, next-header TCP (6) payload length: 66) athena.ssh > fe80::a00:27ff:fe0a:3240.33014:
 Flags [P.], cksum 0xadc5 (incorrect -> 0x3a4f), seq 22:56, ack 6, win 503, options [nop,nop,TS val 1994644623 ecr 849092417], length
 34
11:45:51.551930 IP6 (flowlabel 0x202b7, hlim 64, next-header TCP (6) payload length: 32) fe80::a00:27ff:fe0a:3240.33014 > athena.ssh:
 Flags [.], cksum 0xbc7f (correct), seq 6, ack 56, win 507, options [nop,nop,TS val 849092419 ecr 1994644623], length 0
11:45:51.551938 IP6 (flowlabel 0x9c15b, hlim 64, next-header TCP (6) payload length: 34) athena.ssh > fe80::a00:27ff:fe0a:3240.33014:
 Flags [P.], cksum 0xada5 (incorrect -> 0xaf6e), seq 56:58, ack 6, win 503, options [nop,nop,TS val 1994644624 ecr 849092419], length
 2
11:45:51.552116 IP6 (flowlabel 0x202b7, hlim 64, next-header TCP (6) payload length: 32) fe80::a00:27ff:fe0a:3240.33014 > athena.ssh:
 Flags [.], cksum 0xbc7c (correct), seq 6, ack 58, win 507, options [nop,nop,TS val 849092419 ecr 1994644624], length 0

# Завершение соединения

11:45:51.552265 IP6 (flowlabel 0x9c15b, hlim 64, next-header TCP (6) payload length: 32) athena.ssh > fe80::a00:27ff:fe0a:3240.33014:
 Flags [F.], cksum 0xada3 (incorrect -> 0xbc7e), seq 58, ack 6, win 503, options [nop,nop,TS val 1994644625 ecr 849092419], length 0
11:45:51.552374 IP6 (flowlabel 0x202b7, hlim 64, next-header TCP (6) payload length: 32) fe80::a00:27ff:fe0a:3240.33014 > athena.ssh:
 Flags [F.], cksum 0xbc79 (correct), seq 6, ack 59, win 507, options [nop,nop,TS val 849092419 ecr 1994644625], length 0
11:45:51.552381 IP6 (flowlabel 0x9c15b, hlim 64, next-header TCP (6) payload length: 32) athena.ssh > fe80::a00:27ff:fe0a:3240.33014:
 Flags [.], cksum 0xada3 (incorrect -> 0xbc7d), seq 59, ack 7, win 503, options [nop,nop,TS val 1994644625 ecr 849092419], length 0

14 packets captured
14 packets received by filter
0 packets dropped by kernel
[root@athena ~]#

Теперь попробуем воспользоваться netcat, как сервером: запустим прослушивание порта 1234 (не забыв добавить флаг -6, поскольку по умолчанию будет создаваться IPv4-only сокет):

athena

[root@athena ~]# netcat -6l 1234 &
[1] 779
[root@athena ~]#

При этом в socket stat (ss) будет отображаться информация о новом слушающем соединении

athena

[root@athena ~]# ss -nltp
State       Recv-Q      Send-Q           Local Address:Port             Peer Address:Port      Process
LISTEN      0           128                    0.0.0.0:22                    0.0.0.0:*          users:(("sshd",pid=637,fd=3))
LISTEN      0           1                            *:1234                        *:*          users:(("netcat",pid=779,fd=3))
LISTEN      0           128                       [::]:22                       [::]:*          users:(("sshd",pid=637,fd=4))
[root@athena ~]#

Попробуем подключиться к порту и что-то в него написать:

athena

[root@athena ~]# fg
netcat -6l 1234

boney

[root@boney ~]# netcat fe80::a00:27ff:fe15:1e1c%eth1 1234
hello

athena

[root@athena ~]# fg
netcat -6l 1234
hello

Напишем ответ:

athena

[root@athena ~]# fg
netcat -6l 1234
hello
Bye ye

boney

[root@boney ~]# netcat fe80::a00:27ff:fe15:1e1c%eth1 1234
hello
Bye ye

[root@boney ~]#

Для netcat можно сделать перенаправление вывода данных:

athena

[root@athena ~]# netcat -6l 1234 > file

boney

[root@boney ~]# cal | netcat fe80::a00:27ff:fe15:1e1c%eth1 1234
[root@boney ~]#

athena

[root@athena ~]# netcat -6l 1234 > file
[root@athena ~]#
[root@athena ~]# cat file
      July 2026
Su Mo Tu We Th Fr Sa
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

[root@athena ~]#

UDP-подключение

При работе с UDP-соединением не создаётся отдельное соединение. Вследствие этого задача определения обратных адресов ложится на прикладные протоколы. Как правило, в качестве идентификатора для ответного сообщения указываются адрес отправителя и тот же самый порт, с которого был сделан запрос.

Протестируем работу с UDP-протоколом на примере DNS-запроса:

TODO: Ввести сюда сервер интернета

    `srv#tmux_win2`
    ```console
    [root@srv ~]# tcpdump -nvi eth0 udp
    tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes

    ```

    `srv#tmux_win1`
    ```console
    [root@srv ~]# dig ww.ru

    ; <<>> DiG 9.18.34 <<>> ww.ru
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16655
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

    ;; OPT PSEUDOSECTION:
    ; EDNS: version: 0, flags:; udp: 1232
    ;; QUESTION SECTION:
    ;ww.ru.                         IN      A

    ;; ANSWER SECTION:
    ww.ru.                  30      IN      A       104.21.56.42
    ww.ru.                  30      IN      A       172.67.177.66

    ;; Query time: 114 msec
    ;; SERVER: 10.0.2.3#53(10.0.2.3) (UDP)
    ;; WHEN: Sun Mar 30 21:00:50 UTC 2025
    ;; MSG SIZE  rcvd: 66

    [root@srv ~]#
    ```

    `srv#tmux_win2`
    ```console
    [root@srv ~]# tcpdump -nnvi eth0 udp
    tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
    20:53:59.325961 IP (tos 0x0, ttl 64, id 58418, offset 0, flags [none], proto UDP (17), length 74)
       10.0.2.15.44825 > 10.0.2.3.53: 11283+ [1au] A? ww.ru. (46)
    20:53:59.385749 IP (tos 0x0, ttl 64, id 4, offset 0, flags [none], proto UDP (17), length 94)
       10.0.2.3.53 > 10.0.2.15.44825: 11283 2/0/1 ww.ru. A 172.67.177.66, ww.ru. A 104.21.56.42 (66)

    ```

socat

socat - аналогичная netcat утилита, позволяющая организовать сетевое соединение и передачу данных. Достоинство и одновременная сложность этой утилиты - необходимость описания параметров настраиваемого соединения. Это даёт большую гибкость подключения, однако обращение с утилитой затрудняется в сравнении с netcat.

Пример использования socat - подключение к виртуальной машине в сценарии vbconnect:

socat -,cfmakeraw,echo=0,escape=15 TCP6:localhost:порт

Первая часть параметров описывает параметры принимающей (нашей) стороны (control flow raw mode терминала, отключение дублирования вводящихся символов, отключение от соединения через Ctrl+o), вторая - стороны подключения (По TCP, на локальный адрес и соответствующий порт)

Опишем клиент-серверное подключение на socat:

athena

[root@athena ~]# socat TCP6-LISTEN:1234,reuseaddr STDOUT

Поскольку парсер команд socat использует двоеточие в качестве разделителя, IPv6-адрес описывается в квадратных скобках. boney

[root@boney ~]# socat - TCP6:[fe80::a00:27ff:fe15:1e1c%eth1]:1234

athena

[root@athena ~]# socat TCP6-LISTEN:1234,reuseaddr STDOUT
2026/07/09 12:02:14 socat[805] W address is opened in read-write mode but only supports write-only

Утилита предупреждает что запуск был проведён в режиме write-only (параметр STDOUT), socat Всё равно открывает двусторонний сокет. Поэтому даже с указанием STDOUT мы можем отправлять сообщения в обе стороны:

boney

[root@boney ~]# socat - TCP6:[fe80::a00:27ff:fe15:1e1c%eth1]:1234
qwerty

athena

[root@athena ~]# socat TCP6-LISTEN:1234,reuseaddr STDOUT
2026/07/09 12:02:14 socat[805] W address is opened in read-write mode but only supports write-only
qwerty

athena

[root@athena ~]# socat TCP6-LISTEN:1234,reuseaddr STDOUT
2026/07/09 12:02:14 socat[805] W address is opened in read-write mode but only supports write-only
qwerty
zxcvb
[root@athena ~]#

boney

[root@boney ~]# socat - TCP6:[fe80::a00:27ff:fe15:1e1c%eth1]:1234
qwerty
zxcvb
[root@boney ~]#

Важным параметром является reuseaddr. При закрытии соединения сокет, вообще говоря, не закрывается, а в неиспользуемом состоянии висит в течение тайм-аута. Для того, чтобы пересоздавать уже существующий сокет, используется данный параметр.

Другие транспортные протоколы

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

Одним из первых таких протоколов стал DCCP. Данный протокол подходит для приложений, в которых данные тесно связаны со временем, а потерянные пакеты уже не важны (например, медиавещание, онлайн-игры). В протоколе добавлена интеграция с ECN, приходящие ack-пакеты помечаются предупреждениями о заполнении канала. Основная цель протокола — обеспечить стабильную передачу без старых переотправок (поскольку они неактуальны), не перегружая канал.

Протокол SCTP был одним из первых активно используемых транспортных протоколов, обрабатывающих соединение в несколько параллельных потоков через разные физические соединения (т.н. Multihoming). Транспортный идентификатор теперь не использовал IP, поскольку передача могла вестись по множеству сетевых соединений, вместо этого потоки объединялись по специальному идентификатору «assosiation» и порту. Кроме этого для избежания «SYN flood» атаки в протоколе использовался механизм четырехэтапного рукопожатия.

Непосредственно сам механизм ECN также стал самостоятельным протоколом поддержки TCP-соединений. Он не порождал дополнительного трафика (в отличие от того же SCTP, довольно сильно загружавшего сразу большое количество соединений) и достаточно хорошо прогнозировал перегрузки каналов.

На базе ECN работает ещё один протокол предотвращения перегрузки — L4S. За счёт передачи ECN-данных (дополнительных двух битов в заголовке сетевого уровня; в L4S они обозначают его специализированные сигналы) конечные пользователи могли контролировать скорость передачи данных, что позволяло на участках, поддерживающих L4S, обеспечивать большую пропускную способность. При этом L4S позволяет улучшать пропускной поток при локальном применении без развёртывания на все сегменты сети.

Для абсолютного контроля доступной пропускной способности использовались протоколы резервирования ресурсов на всём маршруте, самым известным из таких протоколов выступает RSVP.

Основной проблемой всех новых протоколов была принципиальная несовместимость их со старыми интерфейсами приложений, настроенными на TCP-соединения. Решением выступил протокол MPTCP. Данный протокол, как и SCTP, строил множество различных физических маршрутов, позволяя оптимизировать передачу данных и избегать перегрузки отдельных каналов. Но его главным преимуществом была полная совместимость с обычными TCP-интерфейсами. В случае неподдержания каким-то из узлов сети данного протокола он мог отбросить нечитаемый предзаголовок MPTCP и обработать передачу обыкновенной TCP-сессии.

Протоколы DCCP и SCTP на текущий момент являются отличными примерами транспортных протоколов, решающих локальные проблемы балансировки потоков. В современной сети требуется поддержка всех подобных решений единым протоколом. При этом создание полностью нового транспортного протокола приведёт к проблемам несовместимости, как в случае STCP. Решением и проблемы совместимости, и проблемы управления потоками сейчас является протокол QUIC. Данный протокол разворачивается поверх UDP, использует его датаграммы для непосредственной передачи данных. Как и в STCP, здесь используется IP-независимый идентификатор ассоциации для отслеживания потоков, поддерживается параллельная передача с независимым контролем и SEQN. Вся передача в QUIC по умолчанию ведётся в зашифрованном виде (подобно TLS версии не ниже 1.3) для защиты данных.

Домашнее задание