Этот протокол кажется простым, но в нем есть несколько деталей, которые следует рассмотреть подробнее. Сегменты могут приходить в неверном порядке. Например, возможна ситуация, в которой байты с 3072-го по 4095-й уже прибыли, но подтверждение для них не может быть выслано, так как байты с 2048-го по 3071-й еще не получены. К тому же сегменты могут задержаться в сети настолько, что у отправителя истечет время ожидания, и он передаст их снова. Переданный повторно сегмент может включать в себя уже другие диапазоны фрагментов, тогда потребуется очень аккуратное администрирование для определения номеров байтов, которые уже были приняты корректно. Но поскольку каждый байт в потоке имеет свое уникальное смещение, эта задача выполнима.
Протокол TCP должен уметь эффективно решать такие проблемы. На оптимизацию производительности TCP-потоков было потрачено много усилий. В следующем разделе мы обсудим несколько алгоритмов, используемых в различных реализациях TCP.
6.5.4. Заголовок TCP-сегмента
На илл. 6.36 показана структура заголовка TCP-сегмента. Каждый сегмент начинается с 20-байтного заголовка фиксированного формата. За ним могут следовать дополнительные параметры. Далее может располагаться до 65 535 – 20 – 20 == 65 495 байт данных (первые 20 байт это IP-заголовок, а вторые — TCP-заголовок). Сегмент может и не содержать данных. Такие сегменты часто применяются для передачи подтверждений и управляющих сообщений.
Илл. 6.36. Заголовок TCP
Рассмотрим поля TCP-заголовка одно за другим. Поля Source port и Destination port указывают локальные конечные точки соединения. TCP-порт вместе с IP-адресом хоста образуют уникальный 48-битный идентификатор конечной точки. Пара конечных точек получателя и отправителя идентифицируют соединение. Такой идентификатор соединения называется кортежем из пяти компонентов (5 tuple), так как он включает пять информационных составляющих: протокол (TCP), IP-адрес отправителя, порт отправителя, IP-адрес получателя и порт получателя.
Поля Sequence number и Acknowledgement number (Номер подтверждения) выполняют свою обычную функцию. Обратите внимание: поле Acknowledgement number относится к следующему по порядку ожидаемому байту, а не к последнему полученному. Это накопительное подтверждение (cumulative acknowledgement), так как один номер объединяет в себе информацию обо всех полученных данных. Сфера его применения не выходит за рамки потерянных данных. Оба поля 32-разрядные, поскольку в TCP-потоке нумеруется каждый байт данных.
Поле TCP header length (Длина TCP-заголовка) сообщает, сколько 32-разрядных слов содержится в TCP-заголовке. Эта информация необходима, так как поле Options, а вместе с ним и весь заголовок имеет переменную длину. По сути, TCP header length указывает смещение от начала сегмента до поля данных, измеренное в 32-битных словах. Это то же самое, что длина заголовка.
Следом идет неиспользуемое 4-битное поле. Тот факт, что эти биты не используются уже 30 лет (изначально поле было 6-битным и из них были задействованы только 2 бита), свидетельствует о том, насколько хорошо продуман дизайн TCP. Иначе протоколы использовали бы эти биты, чтобы справиться с его недостатками.
Затем следуют восемь 1-битных флагов. CWR и ECE сообщают о перегрузках сети в случае, если используется явное уведомление о перегрузке (см. RFC 3168). Когда TCP-получатель узнает, что сеть перегружена, он с помощью флага ECE передает TCP-отправителю сигнал ECN-Echo (ECN-эхо), предлагая ему снизить скорость отправки. Уменьшив скорость, TCP-отправитель сообщает об этом TCP-получателю с помощью флага CWR с сигналом Congestion Window Reduced (Окно перегрузки уменьшено), после чего получатель перестает передавать сигнал ECN-Echo. Подробнее о роли ECN и CWR при контроле перегрузки в TCP мы поговорим в разделе 6.5.10.
Бит URG устанавливается в 1 в случае использования поля Urgent pointer (Указатель срочности), где указано байтовое смещение от текущего порядкового номера до срочных данных. Таким образом, в TCP реализуются прерывающие сообщения. Как уже упоминалось, этот метод позволяет отправителю передать получателю сигнал, не вовлекая в это TCP; он используется редко.
Если бит ACK установлен в 1, значит, поле Acknowledgement number действует. Это справедливо для большинства пакетов. Если ACK установлен в 0, значит, сегмент не содержит подтверждения, и поле Acknowledgement number игнорируется.
Бит PSH является, по сути, PUSH-флагом, с помощью которого отправитель вежливо просит получателя доставить данные приложению сразу, а не хранить их в буфере, пока тот не наполнится (получатель может это делать в целях эффективности).
Бит RST используется для внезапного сброса состояния соединения, которое из-за сбоя хоста или по другой причине попало в тупиковую ситуацию. Также он применяется для отказа от неверного сегмента или от попытки создать соединение. Если в сегменте установлен бит RST, это означает проблему.