Шрифт:
Закладка:
Когда срочные данные приходят по назначению, получающее приложение прерывается (то есть, в терминологии UNIX, «получает сигнал»), затем оно считывает данные из входного потока и ищет среди них срочные. Конец срочных данных маркируется, но их начало приложение должно отыскать самостоятельно.
Эта схема является грубым сигнальным механизмом, который оставляет все прочие задачи приложению. Хотя теоретически использование срочных данных выглядит целесообразным, на заре своего появления эта схема была неудачно реализована и поэтому быстро вышла из употребления. Сейчас использовать ее не рекомендуется из-за различий в имплементации, поэтому приложения вынуждены прибегать к собственным системам сигналов. Возможно, в последующих транспортных протоколах эта идея будет воплощена лучше.
6.5.3. Протокол TCP
В данном разделе мы обсудим TCP в общих чертах, а в следующем — подробно изучим его заголовок.
Ключевым свойством TCP, определяющим всю структуру протокола, является то, что в TCP-соединении у каждого байта есть свой 32-разрядный порядковый номер. В прежние годы, когда типичная скорость выделенных линий между маршрутизаторами составляла 56 Кбит/с, хосту, постоянно работающему на полной скорости, потребовалось бы больше недели на то, чтобы перебрать все порядковые номера. При современных скоростях они могут закончиться пугающе быстро. Отдельные 32-разрядные порядковые номера используются для указания позиции раздвижного окна в одном направлении и для подтверждений в обратном. Все это мы обсудим далее.
Отправляющая и принимающая TCP-подсистемы обмениваются данными в виде сегментов. Сегмент TCP состоит из фиксированного 20-байтного заголовка (плюс необязательная часть), за которым могут следовать байты данных. Размер сегментов определяется программным обеспечением TCP. Оно может объединять в один сегмент данные, полученные в результате нескольких операций записи, или, наоборот, распределять результат одной записи между несколькими сегментами. Их размер ограничен двумя пределами. Во-первых, каждый сегмент, включая TCP-заголовок, должен помещаться в 65 515-байтное поле пользовательских данных IP-пакета. Во-вторых, в каждом канале есть максимальный размер передаваемого блока (Maximum Transfer Unit, MTU). На сторонах отправителя и получателя каждый сегмент должен помещаться в MTU, чтобы он мог передаваться и приниматься в отдельном пакете, не разделенном на фрагменты. На практике MTU обычно составляет 1500 байт (что соответствует размеру поля пользовательских данных Ethernet), и таким образом определяется верхний предел размера сегмента.
Тем не менее фрагментация IP-пакета, содержащего TCP-сегменты, возможна, если на его пути у одного из каналов слишком низкий MTU. Но в таком случае снижается производительность, а также возникают другие проблемы (Кент и Могул; Kent and Mogul, 1987). Вместо этого современные реализации TCP выполняют обнаружение MTU маршрута (path MTU discovery). При этом используется метод, описанный в RFC 1191 (мы говорили о нем в разделе 5.5.6). Этот метод вычисляет минимальное значение MTU по всем каналам пути, используя сообщения об ошибках ICMP. На основе этого значения TCP выбирает размер сегмента, позволяющий избежать фрагментации.
Основной протокол, используемый TCP-подсистемами, — это протокол раздвижного окна с динамическим размером окна. При передаче сегмента отправитель включает таймер. Когда сегмент приходит по назначению, принимающая TCP-подсистема высылает обратно сегмент (с данными, если они есть, или без) с номером подтверждения (он равен порядковому номеру следующего ожидаемого сегмента) и новым размером окна. Если время ожидания подтверждения истекает, отправитель передает сегмент еще раз.
Этот протокол кажется простым, но в нем есть несколько деталей, которые следует рассмотреть подробнее. Сегменты могут приходить в неверном порядке. Например, возможна ситуация, в которой байты с 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.