Компьютерные сети. 6-е изд. - Эндрю Таненбаум
Шрифт:
Интервал:
Закладка:
6.2.5. Мультиплексирование
Мультиплексирование (multiplexing), или объединение нескольких сеансов связи в одном соединении, виртуальном канале и одной физической линии, играет важную роль на нескольких уровнях сетевой архитектуры. На транспортном уровне такая потребность возникает в нескольких случаях. Например, если у хоста имеется только один сетевой адрес, он используется всеми соединениями транспортного уровня. Необходим способ, с помощью которого можно различать, какому процессу нужно передать входящий сегмент. Пример мультиплексирования показан на илл. 6.17 (а): четыре различных соединения транспортного уровня используют одно сетевое соединение (например, один IP-адрес) с удаленным хостом.
Илл. 6.17. Мультиплексирование: (а) прямое; (б) обратное
Мультиплексирование может применяться на транспортном уровне и по другой причине. Предположим, что хост может использовать несколько различных сетевых путей. Если пользователю требуется больше пропускной способности или более высокая надежность, чем может предоставить один сетевой путь, можно создать соединение, распределяющее трафик между путями, используя их поочередно (илл. 6.17 (б)). Такой метод называется обратным мультиплексированием (inverse multiplexing). При открытии k сетевых соединений эффективная пропускная способность может увеличиться в k раз. Примером обратного мультиплексирования является протокол SCTP, позволяющий устанавливать соединение с множественными сетевыми интерфейсами. TCP, наоборот, использует отдельный сокет. Обратное мультиплексирование применяется и на канальном уровне; при этом несколько медленных каналов связи объединяются в один, работающий гораздо быстрее.
6.2.6. Восстановление после сбоев
Если хосты и маршрутизаторы подвержены сбоям или соединения длятся достаточно долго (например, при загрузке мультимедиа или программного обеспечения), вопрос восстановления после сбоев особенно актуален. Если транспортная подсистема полностью размещается в хостах, восстановление после отказов сети и маршрутизаторов не вызывает затруднений. Транспортные подсистемы постоянно ожидают потери сегментов и знают, как с этим бороться: для этого выполняются повторные передачи.
Более серьезную проблему представляет восстановление после сбоя хоста. В частности, клиентам может потребоваться возможность продолжать работу после отказа и быстрой перезагрузки сервера. Чтобы пояснить, в чем тут сложность, предположим, что один хост (клиент) отправляет длинный файл другому хосту (файловому серверу) с помощью простого протокола с остановкой и ожиданием подтверждения. Транспортный уровень сервера просто передает приходящие сегменты один за другим пользователю транспортного уровня. Получив половину файла, сервер сбрасывается и перезагружается, после чего все его таблицы заново инициализируются, поэтому он не знает, на чем он остановился.
Пытаясь восстановить предыдущее состояние, сервер может разослать широковещательный сегмент всем хостам, объявляя, что он только что перезагрузился, и обращаясь с просьбой к своим клиентам сообщить ему о состоянии всех открытых соединений. Каждый клиент находится в одном из двух состояний: один неподтвержденный сегмент (S1) или ни одного неподтвержденного сегмента (S0). Этой информации клиенту должно быть достаточно, чтобы решить, передавать ему повторно последний сегмент или нет.
На первый взгляд все очевидно: узнав о перезапуске сервера, клиент должен передать повторно последний неподтвержденный сегмент. То есть повторная передача требуется, только если клиент находится в состоянии S1. Однако при более детальном рассмотрении выясняется, что все не так просто. Например, рассмотрим ситуацию, в которой транспортная подсистема сервера сначала отправляет подтверждение и лишь затем передает сегмент прикладному процессу. Запись сегмента в выходной поток и отправка подтверждения — это два отдельных события, которые нельзя выполнить одновременно. Если сбой произойдет после отправки подтверждения, но до того, как выполнена запись, клиент получит подтверждение, а при перезапуске сервера окажется в состоянии S0. В результате он не отправит сегмент повторно, так как будет считать, что сегмент уже получен, что приведет к потере сегмента.
Должно быть, вы подумали: «А что, если поменять местами последовательность действий транспортной подсистемы сервера, чтобы сначала осуществлялась запись, а потом высылалось подтверждение?» Представим, что запись сделана, но сбой произошел до отправки подтверждения. Тогда клиент окажется в состоянии S1 и передаст сегмент повторно, а мы получим дубликат сегмента в выходном потоке.
Таким образом, независимо от того, как запрограммированы клиент и сервер, всегда может возникнуть ситуация, в которой протокол не сможет правильно восстановиться. Сервер можно запрограммировать двумя способами: так, чтобы он в первую очередь высылал подтверждение, или так, чтобы он сначала записывал сегмент. Клиент может быть запрограммирован одним из четырех способов: всегда повторно передавать последний сегмент; никогда не передавать повторно последний сегмент; передавать сегмент повторно только в состоянии S0; передавать сегмент повторно только в состоянии S1. Таким образом, получаем восемь комбинаций, но как будет показано далее, для каждой комбинации имеется набор событий, ведущий к ошибке протокола.
На сервере могут происходить три события: отправка подтверждения (A), запись сегмента в выходной процесс (W) и сбой (C). Они могут произойти в виде шести возможных последовательностей: AC(W), AWC, C(AW), C(WA), WAC и WC(A), где скобки означают, что после C события A или W уже не произойдут (ведь уже случился сбой). На илл. 6.18 показаны все восемь комбинаций стратегий сервера и клиента, каждая со своими последовательностями событий. Обратите внимание, что для каждой комбинации существует последовательность, приводящая к ошибке протокола. Например, если клиент всегда передает повторно неподтвержденный сегмент, событие AWC приведет к появлению неопознанного дубликата, хотя при двух других последовательностях протокол будет работать правильно.
Илл. 6.18. Различные комбинации стратегий сервера и клиента
Усложнение протокола не поможет. Даже если клиент и сервер обменяются несколькими сегментами, прежде чем сервер попытается осуществить запись, и клиент будет точно знать, что происходит, у него нет возможности определить, когда произошел сбой: до или после записи. Отсюда следует неизбежный вывод: при жестком условии отсутствия одновременных событий (то есть если отдельные события происходят друг за другом, а не одновременно) более высокие уровни не могут отследить сбой и восстановление хоста.
Все это можно обобщить следующим образом: восстановление после сбоя уровня N может быть осуществлено только уровнем N + 1 и только при условии, что на более высоком уровне сохраняется информация о процессе, достаточная для возвращения в прежнее состояние. Это согласуется с утверждением о том, что транспортный уровень может обеспечить восстановление