
МодельOSI |
|
Стек протоколов Internet |
|
Прикладной уровень |
API функции программы |
|
|
Представительскийуровень |
Трансляция/преобразование |
Прикладной уровень |
|
|
данных |
|
|
Сеансовыйуровень |
Регистрация, безопасность, |
|
|
|
контрольные точки |
|
|
Транспортныйуровень |
Целостность пакетов, |
Межузловойуровень |
TCP |
|
потоковая передача |
|
|
|
|
|
UDP |
Сетевойуровень |
Маршрутизация, адресация, |
Межсетевойуровень |
ICMP |
|
сетевое подключение |
|
|
Канальныйуровень |
Формирование пакетов, |
Неструктурированные пакеты I |
|
Уровеньдоступа |
|
||
|
целостность данных |
|
|
|
|
к сети |
|
Физическийуровень |
Сетевые платы, кабельная |
|
|
|
система, модемы |
|
|
Рис. 5.3. Стек протоколов Internet больше ориентирован на расширение функциональности, а не на инкапсуляцию; как можно заметить, про! токол UDP частично опускается до сетевого уровня модели OSI
Уровень 3: потоки данных (TCP)
Протокол TCP четко соответствует транспортному уровню модели OSI. Он обеспечивает все необходимое для организации сеанса: гарантированную доставку сообщений, потоковую передачу данных, правильный порядок пакетов и обра ботку ошибок. Следует подчеркнуть, что протокол TCP не основан на UDP. У TCP пакета свой заголовок и канал распространения. В табл. 5.3 дается сравне
ние протокола TCP и транспортного уровня модели OSI. |
|
Таблица 5.3. Сравнительные характеристики транспортного уровня и протокола TCP |
|
Транспортный уровень |
TCP |
Надежные данные |
Надежные данные (контрольные суммы) |
Надежная доставка |
Гарантированная доставка |
Согласуемый размер окна |
"Раздвижное" окно |
Ориентирован на записи |
Ориентирован на потоки |
Основан на сетевом уровне |
Основан на протоколе IP |
Глава 5. Многоуровневая сетевая модель |
111 |
Ⱦɚɧɧɚɹ ɜɟɪɫɢɹ ɤɧɢɝɢ ɜɵɩɭɳɟɧɚ ɷɥɟɤɬɪɨɧɧɵɦ ɢɡɞɚɬɟɥɶɫɬɜɨɦ %RRNV VKRS Ɋɚɫɩɪɨɫɬɪɚɧɟɧɢɟ ɩɪɨɞɚɠɚ ɩɟɪɟɡɚɩɢɫɶ ɞɚɧɧɨɣ ɤɧɢɝɢ ɢɥɢ ɟɟ ɱɚɫɬɟɣ ɁȺɉɊȿɓȿɇɕ Ɉ ɜɫɟɯ ɧɚɪɭɲɟɧɢɹɯ ɩɪɨɫɶɛɚ ɫɨɨɛɳɚɬɶ ɩɨ ɚɞɪɟɫɭ piracy@books-shop.com
Уровень 4: прикладной
На прикладном уровне модель TCP/IP заканчивается. Он охватывает сеансо вый, представительский и прикладной уровни модели OSI. На данном уровне ра ботают Web броузеры, шлюзы, Telnet и FTP (File Transfer Protocol — протокол передачи файлов). Технология RPC соответствует представительскому уровню OSI. Сетевая файловая система (NFS) основана на RPC и находится на приклад ном уровне OSI.
Программы прикладного уровня работают с протоколами UDP и TCP, но мо гут принимать сообщения непосредственно от протокола ICMP.
Фундаментальные различия между
моделями OSI и IP
Вмоделях OSI и IP весь стек протоколов распределен по уровням. Обе они ориентированы на то, чтобы не писать машинно зависимый код, а создавать аб страгированные приложения. Тем не менее между ними можно заметить некото рые отличия.
ВOSI по уровням распределены не только протоколы, но и данные. На каж дом уровне к пакету добавляется свой заголовок. По мере того как данные про двигаются вниз по стеку протоколов к физическому уровню, сообщение последо вательно инкапсулируется в пакет все более низкого уровня.
Например, когда посылается сообщение сеансового уровня, на физическом уровне можно будет наблюдать заголовки в таком порядке: канальный, сетевой, транспортный и сеансовый. Вслед за ними в конце блока данных располагается собственно сообщение. Процесс инкапсуляции повторяется на каждом уровне, вплоть до физического. Таким образом, у одного пакета может быть семь заго ловков.
На принимающей стороне происходит обратный процесс: данные последова тельно извлекаются, чтобы определить, следует ли передать сообщение на сле дующий уровень. В предыдущем примере сообщение поступает на физическом уровне. На каждом уровне удаляется соответствующий заголовок. Если за ним обнаруживается еще один заголовок, сообщение передается вверх по стеку. В конце концов, сообщение попадет на сеансовый уровень.
Вмодели IP все происходит по другому. Если на каждом уровне добавлять заголовок, сообщение может стать очень большим, в результате чего снизится пропускная способность. Вместо этого тип протокола указывается в отдельном поле заголовка IP пакета. Когда IP подсистема принимает сообщение, она про веряет данное поле и направляет сообщение непосредственно указанному прото колу, предварительно удалив свой собственный заголовок.
Кроме того, как рассказывалось в конце главы 3, "Различные типы Internet пакетов", каждый протокол из стека TCP/IP играет свою конкретную роль. В от личие от модели OSI, где все протоколы последовательно располагаются друг на
друге, в Internet все они основаны на одном протоколе IP. Протокол ICMP пред назначен для обработки ошибок. Протокол UDP используется для направленной отправки сообщений без установления соединения. Протокол TCP ориентирован
112 |
Часть I. Создание сетевых клиентских приложений |
www.books-shop.com
на потоковую передачу данных. Сам протокол IP предназначен для разработки новых протоколов.
Что чему служит
Теперь необходимо разобраться, как использовать сетевую модель Internet. Чтобы получить доступ к различным уровням стека IP, следует вызвать функцию socket() (табл. 5.4).
Таблица 5.4. Доступ к протоколам семейства Internet |
|
|
Уровень стека TCP/IP |
Программный/пользовательский доступ |
|
4—прикладной |
FTP, Gopher, Lynx, IRC |
|
3 межузловой (TCP) |
socket(PF_INET, SOCK_STREAM, 0); |
|
3 — межузловой (UDP) |
socket(PF_INET, SOCK_DGRAM, 0) , |
|
2 — межсетевой (IСМР) |
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); |
|
2 — межуЗЛОВОЙ (IP) |
socket(PF_INET, SOCK_RAW, протокол); |
|
1— доступ к сети |
socket(PF_INET, |
SOCK_PACKET, фильтр); |
Linux разрешает доступ для чтения к низкоуровневым сообщениям драйверов с помощью функции socket () с аргументом SOCK_PACKET. Благодаря этому можно перехватывать все сообщения, передаваемые по сети. Данная возможность рас сматривалась нами при построении сетевого анализатора в главе 3, "Различные типы Internet пакетов".
Резюме: от теории к практике
В данной главе анализировалась взаимосвязь между различными элементами сети. Рассматривались вопросы аппаратного взаимодействия, подключения к се ти, связи с операционной системой и межзадачного общения. В каждой из этих областей существуют свои проблемы, которые решаются с помощью тщательно разработанных сетевых моделей, предназначенных для упрощения сетевого про граммирования.
В обеих сетевых моделях, OSI и IP, круг решаемых задач разбит на уровни. Все уровни, или слои, основаны друг на друге, подобно прослойкам торта. Пер вый уровень отвечает за организацию физической связи между компьютерами. С каждым следующим уровнем повышается надежность данных, но снижается ско рость взаимодействия.
Глава 5. Многоуровневая сетевая модель |
ИЗ |
www.books-shop.com
www.books-shop.com

Часть
Создание сервер
ных приложений
В этой части...
Глава 6. Пример сервера Глава 7. Распределение нагрузки: многозадачность
Глава 8. Механизмы ввода вывода Глава 9. Повышение производительности Глава 10. Создание устойчивых сокетов
www.books-shop.com
Глава
6
Пример сервера
В этой главе... |
|
Схема работы сокета: общий алгоритм сервера |
121 |
Простой эхо сервер |
122 |
Общие правила определения протоколов |
129 |
Более сложный пример: сервер HTTP |
131 |
Резюме: базовые компоненты сервера |
134 |
www.books-shop.com
В сетевом соединении всегда есть отправитель и получатель. В общем случае отправителем является клиент, который запрашивает сервис, предоставляемый сетевым компьютером. В части I, "Создание сетевых клиентских приложений", рассматривались основы клиентского программирования: как подключить клиен та к серверу, как организовать прямую доставку сообщений без установления со единения и как работать с протоколами стека TCP/IP. С этой главы начинается знакомство с другой стороной соединения — приемником, или сервером.
Чтобы понять схему взаимодействия клиента и сервера, представьте, что сеть — это телефонная система большой компании, в которой сервер является централь ным телефонным номером, направляющим звонки конкретным служащим. Клиент связывается с требуемым служащим, набирая центральный и дополнительный но мера. Теперь ситуация проясняется. Центральный номер является адресом сетевого узла, а дополнительный номер — это порт конкретного сервиса.
Клиент должен знать номер порта, по которому обращается. Это похоже на телефонный номер, который должен быть где то опубликован: если клиент не знает номер, он не сможет по нему позвонить.
Если в части I речь шла о том, как запрашивать сервисы, то теперь мы оста новимся на том, как предоставлять их. В главе шаг за шагом рассматривается процесс создания сервера. В конце главы приводится пример небольшого HTTP сервера, в котором демонстрируется, как связываться с Web клиентом и упаковы вать HTML сообщения.
Схема работы сокета:
общий алгоритм сервера
Процесс построения сервера всегда начинается с создания сокета. Подобно тому как в клиентской программе требуется определенная последовательность системных вызовов, аналогичная последовательность необходима и на сервере, только здесь она длиннее. Если некоторые функции клиенту вызывать не обяза тельно, то для серверного приложения все они нужны (рис. 6.1).
Клиентская программа, которую мы писали в первых главах, вызывала функ ции в такой последовательности: socket(), connect(), read(), write() и close(). Системный вызов bind() был необязательным, так как эту функцию вызывала операционная система. Номер порта не требовался, поскольку программа обра щалась напрямую к серверу. Клиент всегда создает активное соединение, потому что он постоянно его занимает.
С другой стороны, серверные программы должны предоставлять своим клиен там неизменные, четко заданные номера портов. Базовая последовательность вы зовов здесь будет такой: socket(), bind(), listen(), accept() и close(). В то время как клиент создает активное соединение, серверное соединение пассивно. Функ ции listen () и accept () устанавливают соединение только тогда, когда приходит запрос от клиента.
Знакомство с функцией bind() состоялось в главе 4, "Передача сообщений между одноранговыми компьютерами". В этой главе она будет описана более формально. Кроме того, будут представлены две новые функции: listen() и accept().
Глава 6. Пример сервера |
117 |
www.books-shop.com

Рис. 6.1. Алгоритмы построения клиента и сервера сходны, но схема подключения к сети в них разная
Простой эхо сервер
Прежде чем перейти к рассмотрению системных функций, следует рассказать о том, какого рода сервер мы будем создавать. В качестве образца был выбран стандартный эхо сервер. Это основа основ серверного программирования, подоб но приложению "Hello, World" в программировании на языке С. Полный текст примера находится на Web узле в файле simple server.с.
Большинство соединений можно проверить, послав данные и запросив их назад в неизменном виде (эхо). Это хорошая идея для создания простейшего сервера. Аналогичным образом пишутся и отлаживаются даже самые сложные приложения.
Парадигмапостроения и отладки
В сетевом программировании приходится очень много заниматься тестированием и отладкой. Это столь сложная область, что с целью минимизации ошибок следует придерживаться про стейших подходов к построению приложений. Парадигма построения и отладки (одна из состав ных частей методологии ускоренной разработки программ) предписывает сконцентрироваться на решении конкретной проблемы. Когда она будет решена, полученный программный модуль ста нет строительным блоком дляостальной части приложения.
118 |
Часть II. Создание серверных приложений |
www.books-shop.com
В общем случае в серверной программе требуется в определенной последова тельности вызвать ряд системных функций. На примере эхо сервера можно на глядно увидеть эту последовательность, не отвлекаясь на решение других, более специфических задач. Ниже описан общий алгоритм работы эхо сервера.
1.Создание сокета с помощью функции socket().
2.Привязка к порту с помощью функции bind().
3.Перевод сокета в режим прослушивания с помощью функции listen().
4.Проверка подключения с помощью функции accept().
5.Чтение сообщения с помощью функции recv() или read().
6.Возврат сообщения клиенту с помощью функции send() или write ().
7.Если полученное сообщение не является строкой "bye", возврат к п. 5.
8.Разрыв соединения с помощью функции close() или shutdown().
9.Возврат к п. 4.
Вприведенном алгоритме четко видны отличия от протокола UDP и других протоколов, не ориентированных на установление соединений. Здесь сервер не закрывает соединение до тех пор, пока клиент не пришлет команду bye.
Благодаря алгоритму становится понятно, что необходимо предпринять даль ше при создании сервера. Первый очевидный шаг (создание сокета) рассматри вался в главе 1, "Простейший сетевой клиент". Как уже упоминалось, с этого на чинается любая сетевая профамма. Следующий шаг — выбор порта — обязателен для сервера.
Привязка порта к сокету
Работа с ТСР сокетами начинается с вызова функции socket)), которой пере дается константа SOCK_STREAM. Но теперь требуется задать также номер порта, что бы клиент мог к нему подключиться.
Функция bind() спрашивает у операционной системы, может ли профамма за владеть портом с указанным номером. Если сервер не указывает порт, система назначает ему ближайший доступный порт из пула номеров. Этот номер может быть разным при каждом следующем запуске профаммы.
Если профамма запрашивает порт, но не получает его, значит, сервер уже вы полняется. Операционная система связывает порт только с одним процессом.
Объявление функции bind)) выглядит так:
tinclude <sys/socket.h> linclude <resolv.h>
int bind(int sd, struct sockaddr *addr, int addr_size);
Параметр sd является дескриптором ранее созданного сокета. В параметре addr передается структура семейства sockaddr. В ней указывается семейство протоко лов, адрес сервера и номер порта (см. главу 1, "Простейший сетевой клиент"). Последний параметр содержит размер структуры sockaddr. Его необходимо зада вать, потому что такова концепция библиотеки Socket API: один интерфейс, но много архитектур. Операционная система поддерживает множество протоколов, у каждого из которых своя адресная структура.
Перед вызовом функции bind() необходимо заполнить поля структуры sockaddr (листинг 6.1).
Глава 6. Пример сервера |
119 |
www.books-shop.com

Листинг 6.1. Вызов функции bind() в TCP!сервере |
|
|||||
/*** |
|
Пример ТСР сокета: заполнение структуры |
***/ |
|||
/*** |
|
sockaddr_in |
|
|
|
***/ |
struct |
sockaddr_in addr; |
|
/* |
создаем ТСР сокет */ |
||
bzero(&addr, sizeof(addr)); |
|
/* |
обнуляем структуру */ |
|||
addr.sin_family = AF_INET; |
/* |
выбираем стек TCP/IP */ |
||||
addr.sin_port = htons(MY_PORT); |
|
/* задаем номер порта */ |
||||
addr.sin_addr.s_addr = INADDR_ANY; |
|
|
/* любой IP адрес */ |
|||
if ( bind(sd, saddr, sizeof(addr)) != 0 |
) /* запрашиваем порт */ |
|||||
|
perror("Bind AF_INET"); |
|
|
|
|
В следующем фрагменте программы (листинг 6.2) осуществляется инициали зация именованного сокета (семейство AF_UNIX или AFJGOCAL).
Листинг 6.2. Вызов функции bind() в локальном сервере
/*** |
Пример локального сокета: |
заполнение структуры |
***/ |
||||
/*** |
sockaddr_ux |
|
|
|
***/ |
||
#include <linux/un.h> |
|
|
|
|
|||
struct sockaddr_ux addr; /* создаем локальный именованный сокет |
*/ |
||||||
bzero(Saddr, |
sizeof(addr)); |
|
/* обнуляем структуру |
*/ |
|||
addr.sun_family = AF_LOCAL; |
/* выбираем именованные сокеты */ |
||||||
strcpy(addr.sun_path, |
"/tmp/mysocket"); |
/* выбираем имя */ |
|||||
if ( |
bind(sd, |
saddr, |
sizeof(addr)) |
!= 0 ) |
/* привязка к файлу |
*/ |
perror("Bind AF_LOCAL");
Если запустить на выполнение эту программу, то после ее завершения в ката логе /tmp появится файл mysocket. Именованные сокеты используются системным демоном регистрации сообщений, syslogd, для сбора информации: системные процессы устанавливают соединение с сокетом демона и посылают в него сооб щения.
В результате выполнения функции bind() могут возникнуть перечисленные ниже ошибки.
•EBADF. Указан неверный дескриптор сокета. Эта ошибка возникает, если вызов функции socket () завершился неуспешно, а программа не прове рила код ее завершения.
•EACCES. Запрашиваемый номер порта доступен только пользователю root. Помните, что для доступа к портам с номерами 0—1023 программа должна иметь привилегии пользователя root. Подробнее об этом расска
зывалось в главе 2, "Основы TCP/IP".
•EINVAL. Порт уже используется. Возможно, им завладела другая програм ма. Эта ошибка может также возникнуть, если сервер завис и вы тут же запускаете его повторно. Для операционной системы требуется время, чтобы освободить занятый порт (до пяти минут!).
120 |
Часть П. Создание серверных приложений |
www.books-shop.com