📚 Hub Books: Онлайн-чтение книгРазная литератураКомпьютерные сети. 6-е изд. - Эндрю Таненбаум

Компьютерные сети. 6-е изд. - Эндрю Таненбаум

Шрифт:

-
+

Интервал:

-
+
1 ... 186 187 188 189 190 191 192 193 194 ... 335
Перейти на страницу:
сокет на ошибки (определяется по s < 0). В окончательной версии программы сообщение об ошибке может быть чуть более понятным. Вызов setsockopt нужен для того, чтобы порт мог использоваться несколько раз, а сервер — бесконечно, обрабатывая запрос за запросом. Теперь IP-адрес привязывается к сокету и выполняется проверка успешного завершения вызова bind. Конечным этапом инициализации является вызов listen. Он свидетельствует о готовности сервера к приему входящих вызовов и сообщает системе о том, что нужно ставить в очередь до QUEUE_SIZE вызовов, пока сервер обрабатывает текущий вызов. При заполнении очереди прибытие новых запросов спокойно игнорируется.

В этот момент сервер входит в основной цикл программы и уже из него не выходит. Этот цикл можно остановить только извне. Вызов accept блокирует сервер на то время, пока клиент пытается установить соединение. В случае успеха accept возвращает дескриптор сокета, который можно использовать для чтения и записи, аналогично тому, как файловые дескрипторы применяются для чтения и записи в каналы. Однако, в отличие от однонаправленных каналов, сокеты двунаправлены, поэтому для чтения (и записи) данных из соединения можно использовать sa (принятый сокет). Файловые дескрипторы канала могут применяться для чтения или записи, но не одновременно.

После установления соединения сервер считывает имя файла. Если оно еще недоступно, сервер блокируется, ожидая его. Получив имя файла, сервер открывает файл и входит в цикл, который читает блоки данных из файла и записывает их в сокет. Это продолжается до тех пор, пока не будут скопированы все запрошенные данные. Затем файл закрывается, соединение разрывается и начинается ожидание нового вызова. Данный цикл повторяется бесконечно.

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

client flits.cs.vu.nl /usr/tom/filename >f

Этот вызов сработает, только если сервер расположен по адресу flits.cs.vu.nl, файл /usr/tom/filename существует и у сервера есть доступ по чтению для этого файла. Если вызов произведен успешно, файл передается по интернету и записывается в f, после чего клиентская программа заканчивает свою работу. Поскольку серверная программа продолжает работать, клиент может быть запущен снова с новыми запросами на получение файлов.

Клиентская программа начинается с подключения файлов и объявлений. Прежде всего проверяется корректность числа аргументов (где argc = 3 означает, что была вызвана программа с указанием ее имени и двух аргументов). Обратите внимание на то, что argv[1] содержит имя сервера (например, flits.cs.vu.nl) и переводится в IP-адрес с помощью функции gethostbyname. Для поиска имени эта функция использует DNS. Межсетевые экраны мы изучим отдельно в главе 7.

Затем создается и инициализируется сокет, после чего клиент пытается установить TCP-соединение с сервером посредством connect. Если сервер включен, работает на указанном компьютере, соединен с SERVER_PORT и либо простаивает, либо имеет достаточно места в очереди listen (очереди ожидания), то соединение с клиентом будет рано или поздно установлено. По данному соединению клиент передает имя файла, записывая его в сокет.

Число отправленных байтов превышает количество, необходимое для передачи имени, на единицу, поэтому нужен еще нулевой байт-ограничитель, с помощью которого сервер определяет, где кончается имя файла.

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

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

Стоит заметить, что такой сервер построен далеко не по последнему слову техники. Осуществляемая проверка ошибок минимальна, а сообщения об ошибках реализованы весьма посредственно. Система будет обладать низкой производительностью, поскольку все запросы обрабатываются только последовательно (используется один поток запросов). Понятно, что ни о какой защите информации здесь говорить не приходится, а применение аскетичных системных вызовов UNIX не лучшее решение для достижения независимости от платформы. При этом делаются некоторые некорректные с технической точки зрения допущения. Например, что имя файла всегда поместится в буфер и будет передано без ошибок. Несмотря на эти недостатки, с помощью данной программы можно организовать полноценный работающий файл-сервер для интернета. Более подробную информацию вы найдете в работах Донаху и Калверта (Donahoo and Calvert; 2008, 2009), а также Стивенса и др. (Stevens et al., 2004).

6.2. Элементы транспортных протоколов

Транспортные службы реализуются транспортным протоколом, который используется между двумя транспортными подсистемами. Транспортные протоколы несколько напоминают протоколы канального уровня, которые мы подробно рассмотрели в главе 3. И те и другие протоколы, помимо прочего, занимаются обработкой ошибок, управлением очередями и потоками.

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

Илл. 6.7. Окружение: (а) канального уровня; (б) транспортного уровня

Во-первых, при двухточечном соединении по проводу или оптоволоконной линии маршрутизатор обычно не должен указывать, с каким маршрутизатором он хочет обменяться данными, — каждая выходная линия ведет к конкретному маршрутизатору. На транспортном уровне требуется явно указывать адрес получателя.

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

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

1 ... 186 187 188 189 190 191 192 193 194 ... 335
Перейти на страницу:

Комментарии

Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?