47586

Методическое пособие по выполнению практических работ в среде ОС Unix

Книга

Информатика, кибернетика и программирование

Организация взаимодействия между устройствами и программами в сети является сложной задачей. Сеть объединяет разное оборудование, различные операционные системы и программы — их успешное взаимодействие было бы невозможно без принятия общепринятых правил, стандартов

Русский

2013-11-30

185.74 KB

14 чел.

МОСКОВСКИЙ ГОСУДАРСТВЕННЫЙ ОТКРЫТЫЙ УНИВЕРСИТЕТ

Кафедра Вычислительной Техники и Программирования

СЕТИ ЭВМ И ТЕЛЕКОММУНИКАЦИИ

Методическое пособие

по выполнению практических работ в среде ОС Unix

с вводной теоретической частью

Составитель: ст. преп. Найденов В. В.

Москва 2010


Содержание

[0.1] Поддержка сети в ОС UNIX

[0.1.1] Введение в сети

[0.1.1.1] Семиуровневая модель OSI

[0.1.1.2] Протоколы Internet: TCP/IP

[0.1.2] Сетевой интерфейс в UNIX

[0.1.3] Конфигурация IP-сетей

[0.1.3.1] Сетевой адрес

[0.1.3.2] Маршрутизация

[0.1.3.3] Служебный протокол ICMP

[0.1.3.4] Информация о соединениях

[0.1.3.5] Настройка сети при загрузке системы

[0.1.4] Службы Internet

[0.1.4.1] Служба доменных имён

[0.1.4.2] Удалённый терминал

[0.1.4.3] Прокси-серверы

[0.1.5] Межсетевой экран

[0.1.5.1] Концепции iptables

[0.1.6] Резюме

[0.1.7] Вопросы

[0.2] Методические указания по командам управления сетью в UNIX

[0.2.1] Команды по конфигурированию сети

[0.2.2] Команды по диагностике сети

[0.2.3] Команды удалённого терминала

[0.2.4] Команды по управлению сетевым экраном

[0.2.5] Резюме

[0.3] Практическое занятие. Управление сетью

[0.3.1] Сценарий: Настройка сетевого интерфейса

[0.3.2] Сценарий: Настройка таблицы маршрутизации

[0.3.3] Сценарий: Изучение службы доменных имён

[0.3.4] Сценарий: Простая диагностика работы сети

[0.3.5] Сценарий: Работа по удалённому терминалу

[0.3.6] Задания для самоподготовки

[0.4] Сценарии практического занятия на тему: Управление сетью

[0.5] Практическое занятие. Расширенная диагностика и настройка сети

[0.5.1] Сценарий: Сканирование локальной сети

[0.5.2] Сценарий: Сканирование удалённых хостов

[0.5.3] Сценарий: Настройка сетевого экрана

[0.5.4] Задания для самоподготовки

[0.6] Сценарии практического занятия на тему: Расширенная диагностика и настройка сети

[0.7] Примеры прикладных программы сетевого обмена

[0.7.1] sendmail: программа отправления почты

[0.7.1.1] Параметры команды

[0.7.2] wget: консольный загрузчик файлов

[0.7.2.1] Параметры команды

[0.7.2.2] Настройка

[0.8] Использование Socket API для создания сетевых приложений

[0.8.1] Понятие сокета

[0.8.2] Атрибуты сокета

[0.8.3] Адреса

[0.8.4] Установка соединения (сервер)

[0.8.5] Установка соединения (клиент)

[0.8.6] Обмен данными

[0.8.7] Закрытие сокета

[0.8.8] Обработка ошибок

[0.8.9] Отладка программ

[0.8.10] Обмен датаграммами

[0.8.11] Функции для работы с адресами и DNS

[0.8.12] Параллельное обслуживание клиентов

[0.8.12.1] Обслуживание клиентских соединений в параллельных процессах

[0.8.12.2] Мультиплексирование ввода/вывода с помощью функции select

[0.8.13] Работа по стандартным протоколам

[0.9] Практические занятия по использованию Socket API

[0.9.1] Практическое занятие. Простой TCP эхо-клиент и эхо-сервер

[0.9.2] Практическое занятие. Датаграммный эхо-клиент и сервер, протокол UDP

[0.9.3] Практическое занятие. Использование параллельных процессов для параллельной обработки запросов

[0.9.4] Практическое занятие. Использование мультиплексирования ввода/вывода для параллельной обработки запросов

[0.10] Использованная литература


Поддержка сети в ОС UNIX

Сети стали неотъемлемой составляющей современных вычислительных систем. Технологии взаимодействия между информационными системами развиваются с 1970-х годов, ненамного опередив развитие UNIX. Операционная система UNIX почти с самого рождения интегрировала в себя технологии организации локальных сетей, на её основе затем была построена сеть Internet, распространившаяся ныне по всему миру.

Введение в сети

Организация взаимодействия между устройствами и программами в сети является сложной задачей. Сеть объединяет разное оборудование, различные операционные системы и программы — их успешное взаимодействие было бы невозможно без принятия общепринятых правил, стандартов.

В области компьютерных сетей существует множество международных и промышленных стандартов, среди которых следует особенно выделить международный стандарт OSI и набор стандартов IETF (Internet Engineering Task Force).

Семиуровневая модель OSI

В модели OSI, называемой также моделью взаимодействия открытых систем (Open Systems Interconnection — OSI) и разработанной Международной Организацией по Стандартам (International Organization for Standardization — ISO), средства сетевого взаимодействия делятся на семь уровней, для которых определены стандартные названия и функции (см. Рисунок 1. «Уровни ISO OSI»).

Рисунок 1. Уровни ISO OSI

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

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

Рассмотрим подробнее назначение каждого из уровней:

Физический уровень

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

Канальный уровень

Канальный уровень обеспечивает передачу кадра данных между любыми узлами в сетях с типовой топологией либо между двумя соседними узлами в сетях с произвольной топологией, отвечает за установление соединения и корректность доставки данных по физическим каналам. В протоколах канального уровня заложена определенная структура связей между компьютерами и способы их адресации. Адреса, используемые на канальном уровне в локальных сетях, часто называют МАС-адресами (Media Access Control, управление доступом к среде). Часть канального уровня требует аппаратной реализации, в операционной системе он, как правило, представлен драйвером сетевой карты.

Сетевой уровень

Сетевой уровень в первую очередь должен предоставлять средства для решения следующих задач:

  • доставки пакетов в сети с произвольной топологией;
  • структуризации сети путем надежной локализации трафика;
  • согласования различных протоколов канального уровня.

Сетевой уровень позволяет передавать данные между любыми, произвольно связанными узлами сети (при этом не берёт на себя никаких обязательств по надежности передачи данных). Это достигается благодаря наличию адресации — каждый узел в сети имеет свой уникальный идентификатор.

Реализация протокола сетевого уровня подразумевает наличие в сети специального устройства — маршрутизатора. Маршрутизаторы объединяют отдельные сети в общую составную сеть. К каждому маршрутизатору могут быть присоединены несколько сетей (по меньшей мере две).

Транспортный уровень

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

Сеансовый уровень

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

Уровень представления

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

Прикладной уровень

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

Существует несколько групп взаимосвязанных протоколов (называемые также стеки протоколов), которые частично или полностью соответствуют уровням эталонной модели OSI. Слово «стек» отсылает в данном случае к многоуровневой организации группы протоколов, логически взаимосвязанных как лежащие друг над другом «слои». Для нас наибольший интерес представляет стек протоколов TCP/IP.

Протоколы Internet: TCP/IP

Протоколы TCP/IP (Transmission Control Protocol / Internet Protocol) были разработаны по заказу Министерства обороны США 30 лет назад для организации связи в рамках экспериментальной сети ARPAnet и представляет собой набор общих протоколов для разнородной вычислительной среды. Первая реализация стека TCP/IP была создана в стенах университета Беркли для операционнной системы UNIX. Популярность UNIX и удачные идеи, заложенные в TCP/IP, привели к образованию и бурному развитию международной сети Internet. Все протоколы семейства TCP/IP и связанные с ними проходят стандартизацию в организации IETF через выпуск так называемых RFC-документов (Request For Comment).

Протоколы, входящие в TCP/IP, частично соответствуют модели OSI (см. Рисунок 2. «Соответствие стека TCP/IP модели OSI»). Стек TCP/IP поддерживает на нижнем уровне все популярные стандарты физического и канального уровней: для локальных сетей — Ethernet, Token Ring, FDDI, для глобальных — PPP, ISDN. Основными протоколами стека, давшими ему название, являются протоколы IP и TCP, относящиеся к сетевому и транспортному уровням соответственно. IP обеспечивает продвижение пакета по сети, TCP гарантирует надёжность его доставки.

За долгие годы использования стек TCP/IP пополнился множеством протоколов прикладного уровня: FTP, SMTP, HTTP и т. п.

Рисунок 2. Соответствие стека TCP/IP модели OSI

Поскольку стек TCP/IP изначально создавался для глобальной сети, он имеет много особеностей, дающих ему преимущество перед другими протоколами, когда речь заходит о глобальных коммуникациях. Это возможность фрагментации пакетов, гибкая система адресации, простота широковещательных запросов. На сегодня TCP/IP — самый распространённый протокол вычислительных сетей.

Для UNIX-систем TCP/IP исторически являлся первым и остается основным сетевым протоколом. Рассмотрим, как в UNIX осуществляется администрирование сетевых соединений на основе TCP/IP.

Сетевой интерфейс в UNIX

Основной сетевой подсистемы UNIX является сетевой интерфейс. Сетевой интерфейс — это абстракция, связывающая канальный и сетевой (протокол TCP/IP) уровни сети в UNIX.

Сетевой интерфейс создается в момент загрузки драйвера сетевого устройства (например, сетевой карты) или создания логического соединения (например, в случае PPP). Каждый сетевой интерфейс в системе имеет уникальное имя, состоящее из типа устройства и номера (0 или больше для однотипных устройств). Под типами устройств в различных UNIX-системах может пониматься вид протокола канального уровня (Ethenet — eth) или название драйвера устройства (Realtek — rl).

Интерфейс имеет набор параметров, большая часть которых относятся к сетевому уровню (IP-адрес, маска сети и т. п.). Важным параметром сетевого интерфейса является аппаратный адрес (в случае Ethernet аппаратный адрес называется MAC-адрес и состоит из шести байтов, которые принято записывать в шестнадцатеричной системе счисления и разделять двоеточиями).

Основной утилитой для просмотра и управления параметрами сетевых интерфейсов в UNIX служит ifconfig. В настоящее время в ряде систем наряду с ней используется более современная утилита ip, с отличающимся синтаксисом параметров командной строки. Далее в приводимых примерах мы будем использовать в первую очередь традиционную и на сегодняшний день более распространённую утилиту inconfig 

Утилита ifconfig, вызванная в командной строке без параметров, выводит сведения обо всех настроенных в данный момент сетевых интерфейсах. Чтобы просмотреть информацию о конкретном интерфейсе, необходимо указать его имя в качестве параметра ifconfig:

Пример. Просмотр параметров сетевых интерфейсов (ifconfig)

desktop ~ # ifconfig eth0

eth0      Link encap:Ethernet  HWaddr 00:0D:60:8D:42:AA  

         inet addr:192.168.1.5  Bcast:192.168.1.255  Mask:255.255.255.0

         UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

         RX packets:6160 errors:0 dropped:0 overruns:0 frame:0

         TX packets:5327 errors:0 dropped:0 overruns:0 carrier:0

         collisions:1006 txqueuelen:1000

         RX bytes:3500059 (3.3 Mb)  TX bytes:2901625 (2.7 Mb)

         Base address:0x8000 Memory:c0220000-c0240000

desktop ~ # ifconfig lo

lo        Link encap:Local Loopback  

         inet addr:127.0.0.1  Mask:255.0.0.0

         UP LOOPBACK RUNNING  MTU:16436  Metric:1

         RX packets:188 errors:0 dropped:0 overruns:0 frame:0

         TX packets:188 errors:0 dropped:0 overruns:0 carrier:0

         collisions:0 txqueuelen:0

         RX bytes:14636 (14.2 Kb)  TX bytes:14636 (14.2 Kb)

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

Для диагностики трафика на канальном уровне применяется специальные программы. Самыми распространёнными в UNIX являются tcpdump и ethereal. При «прослушивании» канала, эти программы взаимодействуют с заданным сетевым интерфейсом.

Конфигурация IP-сетей

Сетевой адрес

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

Формат адреса зависит от версии протокола. В наиболее распространённой сейчас четвёртой версии адрес состоит из четырёх байт, записываемых традиционно в десятичной системе счисления и разделяемых точкой. Тогда как в шестой версии протокола IP адрес состоит уже из 16 байт и обычно записывается в шестнадцатиричной системе счисления.

IP-адрес сетевого интерфейса eth0 из приведенного выше примера — 192.168.1.5. Второй сетевой интерфейс из примера — lo — так называемая заглушка (loopback), которая используется для организации сетевых взаимодействий компьютера с самим собой: любой посланный в заглушку пакет немедленно обрабатывается как принятый оттуда. Заглушке обычно назначается адрес 127.0.0.1.

Для того чтобы назначить интерфейсу IP-адрес, достаточно выполнить ifconfig, указав после имени интерфейса IP-адрес:

desktop ~ # ifconfig eth0 192.168.1.1

Маршрутизация

Как было сказано выше, IP-адрес включает две части: адрес подсети и адрес конкретного узла в рамках этой подсети. Маска подсети показывает, сколько бит в IP-адресе содержат адрес подсети (остальные — это адрес узла). Таким образом, располагая IP-адресом узла назначения и маской подсети всегда позволяет определить, относится ли узел назначения к той же подсети. В этом случае пакеты к ним будет доставляться напрямую через канальный уровень.

Более сложный вопрос встает, если IP-адрес узла-адресата не входит в локальную сеть узла-отправителя. Ведь и в этом случае пакет необходимо отослать какому-то абоненту локальной сети, с тем, чтобы тот перенаправил его дальше. Этот абонент, маршрутизатор, подключен к нескольким сетям, и ему вменяется в обязанность пересылать пакеты между ними по определенным правилам. В самом простом случае таких сетей две: «внутренняя», к которой подключены компьютеры, и «внешняя», соединяющая маршрутизатор со всей глобальной сетью. Таблицу, управляющую маршрутизацией пакетов, можно просмотреть с помощью утилиты netstat -r или route (обе утилиты имеют ключ -n, заставляющий их использовать в выдаче IP-адреса, а не имена компьютеров):

Пример. Вывод таблицы маршрутизации (route)

desktop ~ # route -n

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0

127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        0 lo

0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 eth0

Компьютер или аппаратное устройство, осуществляющее маршрутизацию между локальной сетью и Internet обычно называется шлюзом.

Для изменения таблицы маршрутизации используется та же утилита route. Утилита ip объединяет возможности как по настройке сетевых интерфейсов, так и таблицы маршрутизации.

Служебный протокол ICMP

Есть такие протоколы уровня IP, действие которых этим уровнем и ограничивается. Например, служебный протокол ICMP (Internet Control Message Protocol), предназначенный для передачи служебных сообщений.

Примером применения ICMP является утилита ping, которая позволяет проверить работоспособность узлов в сети. Другое применение ICMP — сообщать отправителю, почему его пакет невозможно доставить адресату, или передавать информацию об изменении маршрута, о возможности фрагментации и т. п.

Ещё одна задача, которую можно решить использованием протокола ICMP, является определение маршрута следования пакета. В UNIX существует ряд альтернативных команд для определения приблизительного маршрута прохождения пакета по сети: traceroute, tracepath и т. п.

Пример. Выполнение команды traceroute

desktop ~ # traceroute ya.ru

traceroute to ya.ru (213.180.204.8), 64 hops max, 40 byte packets

 1  195.91.230.65 (195.91.230.65)  0.890 ms  1.907 ms  0.809 ms

 2  cs7206.rinet.ru (195.54.192.28)  0.895 ms  0.769 ms  0.605 ms

 3  ix2-m9.yandex.net (193.232.244.93)  1.855 ms  1.519 ms  2.95 ms

 4  c3-vlan4.yandex.net (213.180.210.146)  3.412 ms  2.698 ms  2.654 ms

 5  ya.ru (213.180.204.8)  2.336 ms  2.612 ms  3.482 ms

Утилита traceroute показывает список абонентов, через которых проходит пакет по пути к адресату, и затраченное на это время. Однако список этот приблизительный. Строго говоря, неизвестно, каким маршрутом шла очередная группа пакетов, потому что с тех пор, как была отправлена предыдущая группа, какой-нибудь из промежуточных маршрутизаторов мог изменить решение и отправить новые пакеты другим путем.

Информация о соединениях

Транспортных протоколов в TCP/IP два — это TCP (Transmission Control Protocol, протокол управления соединением) и UDP (User Datagram Protocol). UDP обеспечивает более высокую скорость обмена данными за счёт простоты устройства. Пользовательские данные помещаются в единственный транспортный пакет-датаграмму, которой приписываются обычные для транспортного уровня данные: адреса и порты отправителя и получателя, после чего пакет уходит в сеть искать адресата. Проверять, был ли адресат способен этот пакет принять, дошел ли пакет до него и не испортился ли по дороге, предоставляется следующему — прикладному — уровню.

Другое дело — TCP. Этот протокол очень заботится о том, чтобы передаваемые данные дошли до адресата в целости и сохранности. Для этого предпринимаются следующие действия:

  • установление соеднинения;
  • обработка подтверждения корректной доставки;
  • отслеживание состояния абонентов.

Для просмотра всех существующих в настоящий момент сетевых соединений можно воспользоваться командой netstat:

Пример. Выполнение команды netstat

desktop ~ # netstat -an

Active Internet connections (servers and established)

Proto Recv-Q Send-Q Local Address           Foreign Address         State

tcp        0      0 0.0.0.0:32769           0.0.0.0:*               LISTEN

tcp        0      0 0.0.0.0:32770           0.0.0.0:*               LISTEN

tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN

tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN

tcp        0      0 192.168.11.5:34949      83.149.196.70:5223      ESTABLISHED

tcp        0      0 192.168.11.5:39833      213.248.55.180:5223     ESTABLISHED

tcp        0      0 192.168.11.5:59577      192.168.11.1:22         TIME_WAIT

udp        0      0 0.0.0.0:32768           0.0.0.0:*

udp        0      0 0.0.0.0:32769           0.0.0.0:*

udp        0      0 0.0.0.0:111             0.0.0.0:*

Согласно стандартам Internet для большинства протоколов прикладного уровня существуют стандартные порты, на которых соответствующие приложения должны принимать соединения. Например, веб-сервер, выполняющий обработку соединений по протоколу HTTP, должен работать на порту с номером 80.

В UNIX существует прозрачный механизм именования протоколов прикладного уровня. В файле /etc/services можно увидеть список соответствия имён протоколов номерам портов. Этот файл используется базовой системной библиотекой сетевого взаимодействия, так что во всех утилитах вместо номера порта можно указывать имя соответствующего протокола.

Настройка сети при загрузке системы

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

Службы Internet

Служба доменных имён

В предыдущих примерах использовался ключ -n многих сетевых утилит, чтобы избежать путаницы между IP-адресами и доменными именами компьютеров. С другой стороны, доменные имена — несколько слов (часто осмысленных), — запоминать гораздо удобнее, чем адреса (четыре числа).

Когда-то имена всех компьютеров в сети, соответствующие IP-адресам, хранились в файле /etc/hosts. Пока абоненты Internet были наперечет, поддерживать правильность его содержимого не составляло труда. Как только сеть начала расширяться, неувязок стало больше. Трудность была не только в том, что содержимое hosts быстро менялось, но и в том, что за соответствие имен адресам в различных сетях отвечали разные люди и разные организации. Появилась необходимость структурировать глобальную сеть не только топологически (с помощью IP и сетевых масок), но и административно, с указанием, за какие группы адресов кто отвечает.

Проще всего было структурировать сами имена компьютеров. Вся сеть была поделена на домены — зоны ответственности отдельных государств («us», «uk», «ru», «it» и т. п.) или независимые зоны ответственности («com», «org», «net», «edu» и т. п.). Для каждого из таких доменов первого уровня должно присутствовать подразделение, выдающее всем абонентам имена, заканчивающиеся на «.домен» Подразделение обязано организовать и поддерживать службу, заменяющую файл hosts: любой желающий имеет право узнать, какой IP-адрес соответствует имени компьютера в этом домене или какому доменному имени соответствует определенный IP-адрес.

Такая служба называется DNS (Domain Name Service, служба доменных имен). Она имеет иерархическую структуру. Если за какую-то группу абонентов домена отвечают не хозяева домена, а кто-то другой, ему выделяется поддомен (или домен второго уровня), и он сам распоряжается именами вида «имя_компьютера.поддомен.домен». Таким образом, получается нечто вроде распределенной сетевой базы данных, хранящей короткие записи о соответствии доменных имен IP-адресам.

В самом простом случае для того, чтобы сказать системе, какой сервер доменных имён использовать, необходимо изменить файл /etc/resolv.conf. В более сложных системах можно установить и настроить собственный сервер доменных имён.

Для проверки работы системы DNS используются утилиты dig и host.

Удалённый терминал

Концепция терминала, может быть расширена с учетом подключения к локальной и глобальной сети. Действительно, было бы удобно получить доступ к удалённой машине и работать с ней как за обычными монитором и клавиатурой. Отпадает необходимость иметь аппаратный терминал к каждому компьютеру, можно получить терминал на одном компьютере, затем получить доступ к удаленному терминалу по сети на любой другой компьютер. В современных условиях повсеместного распространения сети Internet удалённые терминалы становятся основным рабочим инструментом администратора системы.

В сетях TCP/IP существует несколько приложений, предоставляющих доступ к системе посредством удаленного терминала. Рассмотрим два из них: telnet и ssh. Оба построены по единому клиент-серверному принципу.

Telnet — стандартное приложение, которое присутствует практически в каждой реализации TCP/IP. Оно может быть использовано для связи между узлами, работающими под управлением различных операционных систем. Telnet использует согласование настроек клиента и сервера, чтобы определить характеристики системы с той и с другой стороны.

Клиент telnet взаимодействует и с пользователем, находящимся за терминалом, и с протоколами TCP/IP. Обычно все, что пользователь вводит с клавиатуры, отправляется по TCP-соединению, а все, что приходит по соединению, выводится на терминал.

Сервер telnet обычно взаимодействует с так называемыми псевдотерминальными устройствами в UNIX системах. Это делает его похожим на командный интерпритатор (shell), который запускается на сервере, или на любую программу, которая запускается из командной оболочки, так как они аналогичным образом взаимодействуют с терминальными устройствами.

Клиент telnet имеет еще одно полезное применение. С его помощью можно тестировать стандартные сетевые протоколы — если в качестве порта назначения указать порт соответствующего приложения. Telnet пересылает текстовые строки, разделённые знаками переноса строки, что делает его совместимым со многими протоколами Internel (SMTP, HTTP и т. п.).

Программа telnet обладает значительным недостатком — вся передаваемая информация (в том числе аутентификация пользователей) пересылается открытым текстом. В современных условиях глобальной сети это уже небезопасно. Эту проблемы решает программа ssh, которая предоставляет функциональность, аналогичную telnet, но при этом устанавливает защищённое соединение, в котором все передаваемые данные зашифрованы.

В настоящее время для удалённого администрирования серверов в Internet повсеместно применяется ssh.

Прокси-серверы

Прокси-сервер — специальная служба, расположенная между локальной сетью и Internet, которая обеспечивает доступ в Internet по протоколам прикладного уровня (HTTP, FTP и т. п.) для всех узлов локальной сети. Такой сервер может поддерживать аутентификацию пользователей, учёт и фильтрацию трафика.

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

Межсетевой экран

В UNIX существует мощный механизм анализа сетевых и транспортных пакетов, позволяющий избавляться от нежелательной сетевой активности, манипулировать потоками данных и даже преобразовывать служебную информацию в них. Обычно такие средства носят название firewall («fire wall» — противопожарная стена, брандмауэр), общепринятый русский термин — межсетевой экран.

В различных версиях UNIX функциональность и управление межсетевым экраном может значительно различаться. В семействе операционных систем BSD используются программы ipfw и pf. В данном курсе будет рассматриваться приложение iptables, которое используется для организации межсетевого экрана в Linux.

Концепции iptables

Суть iptables в следующем. Обработка сетевого пакета системой представляется как его конвейерная обработка. Пакет нужно получить из сетевого интерфейса или от системного процесса, затем следует выяснить предполагаемый маршрут этого пакета, после чего отослать его через сетевой интерфейс либо отдать какому-нибудь процессу, если пакет предназначался нашему компьютеру. Налицо три конвейера обработки пакетов: «получить — маршрутизировать — отослать» (действие маршрутизатора), «получить — маршрутизировать — отдать» (действие при получении пакета процессом) и «взять — маршрутизировать — отослать» (действие при отсылке пакета процессом).

Между каждыми из этих действий системы помещается модуль межсетевого экрана, именуемый цепочкой. Цепочка обрабатывает пакет, исследуя, изменяя и даже, возможно, уничтожая его. Если пакет уцелел, она передает его дальше по конвейеру. В этой стройной схеме есть два исключения. Во-первых, ядро Linux дает доступ к исходящему пакету только после принятия решения о его маршрутизации, поэтому связка «взять — маршрутизировать» остается необработанной, а цепочка, обрабатывающая исходящие пакеты (она называется OUTPUT) вставляется после маршрутизации. Во-вторых, ограничения на «чужие» пакеты, исходящие не от нас и не для нас предназначенные, существенно отличаются от ограничений на пакеты «свои», поэтому после маршрутизации транзитные пакеты обрабатываются еще одной цепочкой (она называется FORWARD). Цепочка, обслуживающая связку «получить — маршрутизировать», называется PREROUTING, цепочка, обслуживающая связку «маршрутизировать — отдать» — INPUT, а цепочка, стоящая непосредственно перед отсылкой пакета — POSTROUTING (см. Рисунок 3. «Обработка пакета в iptables»).

Рисунок 2.35. Обработка пакета в iptables

Каждая цепочка представляет собой список правил, последовательно применяемых к анализируемому пакету. Каждое правило описывает некоторый набор свойств пакета и указывает действие, которое нужно произвести над пакетом с такими свойствами. Если пакет не имеет свойств, задаваемых первым правилом, к нему применяется второе, если второе также не подходит — третье, и так вплоть до последнего, правила по умолчанию, которое применяется к любому пакету. Если свойства пакета удовлетворяют правилу, над ним совершается указанное в правиле действие. Действие DROP уничтожает пакет, а действие ACCEPT немедленно выпускает его из таблицы, после чего пакет движется дальше по конвейеру. Некоторые действия, например LOG, никак не влияют на судьбу пакета, после их выполнения он остается в таблице: к нему применяется следующее правило, и т. д. до ACCEPT или DROP.

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

Следует помнить, что чем больше транспортных соединений отслеживается межсетевым экраном, тем больше требуется оперативной памяти ядру Linux и тем медленнее работает процедура сопоставления проходящих пакетов таблице. Впрочем, мощность современных компьютеров позволяет без каких-либо затруднений обслуживать преобразование адресов для сети с пропускной способностью 100Мбит/с и даже выше.

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

Резюме

Сеть состоит из различных аппаратно-программных узлов, для объединения которых используются стандартные протоколы. Эталонной моделью взаимодействия таких систем является семиуровневая модель OSI.

Протокол TCP/IP частично реализует уровни OSI. Основные протоколы IP (сетевого уровня) и TCP (транспортного уровня) позволили объединить разрозненные локальные сети в глобальную сеть Internet.

В UNIX основной сетевого взаимодействия является интерфейс, который находится между канальным и сетевым уровнем. Конфигурация TCP/IP включает в себя настройку интерфейса, маршрутизации и сервисов Internet, в первую очередь сервера доменных имен.

Для удаленного управления компьютерами используется программа telnet и её современных защищённый аналог — ssh.

Важным элементом сетевой инфраструктуры является межсетевой экран, который позволяет ограничить сетевой трафик и изменить его свойства. На лекции был рассмотрен межсетевой экран Linux — iptables.

Ключевые термины: OSI, физический уровень, канальный уровень, МАС-адрес, сетевой уровень, маршрутизатор, транспортный уровень, сеансовый уровень, уровень представления, прикладной уровень, сетевой интерфейс, IP-адрес, шлюз, служба доменных имен, удалённый терминал, прокси-сервер, межсетевой экран, цепочка, NAT

Вопросы

  1.  Какие уровни входят в модель ISO OSI? Какие можно провести аналогии с реально существующими протоколами?
  2.  Что такое сетевой интерфейс в UNIX? Для чего он используется и каким образом настраивается?
  3.  Как управлять IP-маршрутизацией в UNIX?
  4.  Что такое служба доменных имён в UNIX? Как она конфигурируется?
  5.  Какие функции выполняет межсетевой экран? Каковы принципы управления межсетевым экраном iptables?


Методические указания по командам управления сетью в UNIX

Команды по конфигурированию сети

ifconfig 

Команда используется для настройки сетевых интерфейсов

Команда ifconfig имеет следующий синтаксис:

ifconfig [-L] [-m] interface [create] [address_family] [address [dest_address]] [parameters]

ifconfig interface destroy

ifconfig -a [-L] [-d] [-m] [-u] [address_family]

ifconfig -l [-d] [-u] [address_family]

ifconfig [-L] [-d] [-m] [-u] [-C]

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

user@desktop$ ifconfig rl0

rl0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500

          options=8<VLAN_MTU>

          inet6 fe80::250:22ff:febb:5f1%rl0 prefixlen 64 scopeid 0x3

          inet 192.168.19.86 netmask 0xffffff00 broadcast 192.168.19.255

          ether 00:50:22:bb:05:f1

          media: Ethernet autoselect (100baseTX <full-duplex>)

          status: active

Иначе команда конфигурирует указанный интерфейс. Изменить настройки какого-либо интерфейса может только суперпользователь.

Опции:

интерфейс

 – имя интерфейса (например, rl0 в BSD или eth0 в Linux).

up

 – вызывает активизацию интерфейса. Задается неявно при присвоении адреса интерфейсу.

down

 – вызывает остановку работы драйвера для интерфейса.

[-]arp

 – включает или отключает использование протокола ARP для интерфейса.

[-]promisc

 – включает или отключает неразборчивый режим (promiscuous mode) работы интерфейса. В этом режиме все проходящие по сети пакеты будут приниматься интерфейсом.

[-]allmulti

 – включает или отключает режим all-multicast. В этом режиме все многоадресные (multicast) пакеты в сети будут приниматься интерфейсом.

metric N

 – устанавливает метрику интерфейса.

mtu N

 – устанавливает максимальный размер пакета (Maximum Transfer Unit - MTU) для интерфейса.

адрес

 – IP-адрес, присваиваемый интерфейсу.

netmask адрес

 – устанавливает маску сети IP для этого интерфейса. По умолчанию используется обычная маска сети класса A, B или C (что определяется по IP-адресу интерфейса), но можно усановить любое значение.

add адрес/длина_префикса

 – добавляет адрес IPv6 для интерфейса.

del адрес/длина_префикса

 – удаляет адрес IPv6 для интерфейса.

irq адрес

 – устанавливает аппаратное прерывание, используемое устройством. Не для всех устройств можно динамически менять значение IRQ.

media тип

 – устанавливает физический порт или тип носителя, используемый устройством. Не для всех устройств можно менять этот параметр, и для разных устройств могут поддерживаться различные значения. Типичные значения типа - 10base2 (коаксиальный кабель Ethernet), 10baseT (витая пара Ethernet 10 Мбит/сек), AUI (внешний передатчик) и т.д. Специальный тип носителя auto можно использовать, чтобы потребовать от драйвера автоматически обпределять тип носителя. Не все драйверы могут это делать.

[-]broadcast [адрес]

 – если указан аргумент адрес, задает соответствующий протоколу широковещательный адрес для интерфейса. В противном случае устанавливает (или сбрасывает) флаг IFF_BROADCAST для интерфейса.

Пример. изменение IP-адреса интерфейса rl0:

user@desktop ~ $ ifconfig rl0

rl0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500

          options=8<VLAN_MTU>

          inet6 fe80::250:22ff:febb:5f1%rl0 prefixlen 64 scopeid 0x3

          inet 192.168.19.86 netmask 0xffffff00 broadcast 192.168.19.255

          ether 00:50:22:bb:05:f1

          media: Ethernet autoselect (100baseTX <full-duplex>)

          status: active

user@desktop ~ $ ifconfig rl0 192.168.0.1

user@desktop ~ $ ifconfig rl0

rl0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500

          options=8<VLAN_MTU>

          inet6 fe80::250:22ff:febb:5f1%rl0 prefixlen 64 scopeid 0x3

          inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.19.255

          ether 00:50:22:bb:05:f1

          media: Ethernet autoselect (100baseTX <full-duplex>)

          status: active

arp 

Команда arp отображает ARP-таблицу данного хоста. С помощью параметра -i можно специфицировать сетевой интерфейс, информация о котором интересует.

desktop ~ # arp -i eth0

Address                  HWtype  HWaddress           Flags Mask            Iface

DIMON.mshome.net         ether   00:50:BF:12:8A:9E   C                     eth0

Таблица с информацией о канальном уровне содержит связь IP- и MAC-адресов. При использовании параметра -n IP-адреса не будут заменяться символьными именами хостов.

route 

Эта команда используется для просмотра и изменения таблицы маршрутизации хоста. Для этой команды также работает параметр -n, при использовании которого IP-адреса не будут заменяться символьными именами хостов.

Пример обычной таблицы маршрутизации для отдельного компьютера в сети:

desktop ~ # route -n

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

192.168.5.0     0.0.0.0         255.255.255.0   U     0      0        0 eth1

127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        0 lo

0.0.0.0         192.168.5.254   0.0.0.0         UG    0      0        0 eth1

Особый интерес представляет адрес 0.0.0.0, который соответствует хосту назначения по умолчанию.

Для добавление нового маршрута к определённому хосту используются параметры add и -host:

desktop ~ # route add -host 192.168.0.1 eth0

Эта команда создаёт новую строку в таблице маршрутизации, согласно которой все пакеты к узлу 192.168.0.1 должны отправляться в сетевой интерфейс eth0.

Также можно добавлять шлюз для отправки пакетов в определённую сеть или к хосту:

desktop ~ # route add -net 192.168.1.0 gw 192.168.0.5

Таким образом, все пакеты для сети 192.168.1.0 будут направляться на узел 192.168.0.5.

Аналогично, маршруты удаляются параметром del с указанием всей информации о маршруте:

desktop ~ # route del default gw 192.168.0.1

Эта команда удаляет маршрут по умолчанию через хост 192.168.0.1.

Команды по диагностике сети

ping 

Команда используется для посылки пакетов ICMP ECHO_REQUEST сетевым хостам.

Команда ping имеет следующий синтаксис:

ping [-AaDdfnoQqRrv] [-c число_пакетов] [-i секунд] [-l preload] [-M mask | time]

               [-m ttl] [-P policy] [-p pattern] [-S src_addr] [-s packetsize]

               [-t timeout] [-z tos] host

ping [-AaDdfLnoQqRrv] [-c число_пакетов] [-I iface] [-i секунд] [-l preload]

               [-M mask | time] [-m ttl] [-P policy] [-p pattern] [-S src_addr]

               [-s packetsize] [-T ttl] [-t timeout] [-z tos] mcast-group

Команда ping использует датаграмму ECHO_REQUEST протокола ICMP, чтобы вызвать ответ ICMP ECHO_RESPONSE указанного хоста или сетевого шлюза. Если хост отвечает, ping выдает сообщение, что хост включен (хост is alive), в стандартный выходной поток.

Для проверки наличия хоста в сети достаточно ввести команду ping с аргументом - именем или адресом хоста:

user@desktop$ ping yandex.ru

64 bytes from 213.180.204.11: icmp_seq=0 ttl=48 time=5.659 ms

64 bytes from 213.180.204.11: icmp_seq=1 ttl=48 time=5.404 ms

64 bytes from 213.180.204.11: icmp_seq=2 ttl=48 time=4.889 ms

^C

--- yandex.ru ping statistics ---

3 packets transmitted, 3 packets received, 0% packet loss

round-trip min/avg/max/stddev = 4.889/5.317/5.659/0.320 ms

Для отправки определенного числа пакетов необходимо указать опцию -c число_пакетов. Для установки интервала между отправкой пакетов используется опция -i секунд.

traceroute 

Команда traceroute служит для отладки сетевых соединений посредством построения маршрута следования пакетов к хосту назначения. Для этой команды также работает параметр -n, при использовании которого IP-адреса не будут заменяться символьными именами хостов.

Пример следования пакетов до хоста ya.ru:

desktop ~ # traceroute ya.ru

traceroute to ya.ru (213.180.204.8), 64 hops max, 40 byte packets

 1  195.91.230.65 (195.91.230.65)  0.890 ms  1.907 ms  0.809 ms

 2  cs7206.rinet.ru (195.54.192.28)  0.895 ms  0.769 ms  0.605 ms

 3  ix2-m9.yandex.net (193.232.244.93)  1.855 ms  1.519 ms  2.95 ms

 4  c3-vlan4.yandex.net (213.180.210.146)  3.412 ms  2.698 ms  2.654 ms

 5  ya.ru (213.180.204.8)  2.336 ms  2.612 ms  3.482 ms

netstat 

Команда используется для показа состояния сети.

Команда netstat имеет следующий синтаксис:

netstat [-AaLnSW] [-f protocol_family | -p protocol] [-M core] [-N system]

Команда netstat показывает содержимое различных структур данных, связанных с сетью, в различных форматах в зависимости от указанных опций. Первая форма команды показывает список активных сокетов (sockets) для каждого протокола. Вторая форма выбирает одну из нескольких других сетевых структур данных. Третья форма показывает динамическую статистику пересылки пакетов по сконфигурированным сетевым интерфейсам; аргумент интервал задает, сколько секунд собирается информация между последовательными показами.

-p имя_протокола Ограничить показ статистики или адресов управляющих блоков только протоколом с указанным именем_протокола, например, tcp.

Опции:

-a

 – показывать состояние всех сокетов; обычно сокеты, используемые серверными процессами, не показываются.

-A

 – показывать адреса любых управляющих блоков протокола, связанных с сокетами; используется для отладки.

-i

 – показывать состояние автоматически сконфигурированных (auto-configured) интерфейсов. Интерфейсы, статически сконфигурированные в системе, но не найденные во время загрузки, не показываются.

-n

 – показывать сетевые адреса как числа. netstat обычно показывает адреса как символы. Эту опцию можно использовать с любым форматом показа.

-r

 – показать таблицы маршрутизации. При использовании с опцией -s, показывает статистику маршрутизации.

-s

 – показать статистическую информацию по протоколам. При использовании с опцией -r, показывает статистику маршрутизации.

-f семейство_адресов

 – ограничить показ статистики или адресов управляющих блоков только указанным семейством_адресов, в качестве которого можно указывать:

inet

Для семейства адресов AF_INET

unix

Для семейства адресов AF_UNIX

-I интерфейс

 – выделить информацию об указанном интерфейсе в отдельный столбец; по умолчанию (для третьей формы команды) используется интерфейс с наибольшим объемом переданной информации с момента последней перезагрузки системы. В качестве интерфейса можно указывать любой из интерфейсов, перечисленных в файле конфигурации системы, например, emd1 или lo0.

-p имя_протокола

 –  Ограничить показ статистики или адресов управляющих блоков только протоколом с указанным именем_протокола, например, tcp.

Пример. показ таблицы маршрутизации:

user@desktop ~$ netstat -r

Routing tables            

Internet:

Destination        Gateway            Flags    Refs      Use  Netif Expire

default            19-101.local       UGS         0  1373769    rl0

localhost          localhost          UH          1      290    lo0

192.168.0          link#1             UC          0        0    dc0

192.168.19         link#3             UC          0        0    rl0

19-86.local        localhost          UGHS        0        0    lo0

19-101.local       00:0d:bc:e4:27:bf  UHLW        1        0    rl0    116

Internet6:

Destination        Gateway            Flags      Netif Expire

localhost.prov.ru  localhost.prov.ru  UH          lo0

fe80::%dc0         link#1             UC          dc0

fe80::2a0:ccff:fe3 00:a0:cc:3d:1f:bd  UHL         lo0

fe80::%rl0         link#3             UC          rl0

fe80::250:22ff:feb 00:50:22:bb:05:f1  UHL         lo0

fe80::%lo0         fe80::1%lo0        U           lo0

fe80::1%lo0        link#5             UHL         lo0

ff01::             localhost.prov.ru  U           lo0

ff02::%dc0         link#1             UC          dc0

ff02::%rl0         link#3             UC          rl0

ff02::%lo0         localhost.prov.ru  UC          lo0

host 

Команда host служит для получения доменной информации о хосте: IP-адрес, MX-записи и другой информации, связанной с данным символьным именем. Имя хоста указывается в качестве аргумента команды.

Пример работы команды:

user@desktop ~$ host yandex.ru

yandex.ru has address 213.180.204.11

yandex.ru mail is handled by 10 mx2.yandex.ru.

yandex.ru mail is handled by 0 mx1.yandex.ru.

Вторым аргументом можно указать DNS-сервер, который будет использоваться при получении этой информации:

user@desktop ~$ host yandex.ru ns1.aiya.ru

Using domain server:

Name: ns1.aiya.ru

Address: 85.142.20.152#53

Aliases:

yandex.ru has address 213.180.204.11

Using domain server:

Name: ns1.aiya.ru

Address: 85.142.20.152#53

Aliases:

Using domain server:

Name: ns1.aiya.ru

Address: 85.142.20.152#53

Aliases:

yandex.ru mail is handled by 0 mx1.yandex.ru.

yandex.ru mail is handled by 10 mx2.yandex.ru.

tcpdump 

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

Параметр -i задаёт имя сетевого интерфейса, на котором запускается прослушивание. При просмотре захватываемых данных удобно использовать ключ -l, который буферезует вывод построчно. Для этой команды также работает параметр -n, при использовании которого IP-адреса не будут заменяться символьными именами хостов. Пример работы команды:

desktop ~ # tcpdump -i eth0 -l -n

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode

listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes

12:51:07.486755 arp who-has 0.0.0.0 (00:30:48:2b:6d:6a) tell 0.0.0.0

12:51:12.486606 arp who-has 0.0.0.0 (00:30:48:2b:6d:6a) tell 0.0.0.0

12:51:14.457608 IP 192.168.5.23.56385 > 194.91.250.11.443: P 3645922938:3645923156(218) ack 2092518729 win 10086

12:51:14.491343 IP 194.91.250.11.443 > 192.168.5.23.56385: . ack 218 win 10720

Для вывода расширенной информации о пакетах исплюзуются ключи -v или -vv.

desktop ~ # tcpdump -i eth1 -l -n -vv

tcpdump: listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes

12:57:53.043797 IP (tos 0x0, ttl  51, id 46031, offset 0, flags [DF], proto: TCP (6),

 length: 286) 194.91.250.11.5190 > 192.168.5.23.38993: P 2517343058:2517343292(234)

 ack 2346573376 win 2202 <nop,nop,timestamp 2713588760 497668>

12:57:53.043865 IP (tos 0x0, ttl  64, id 52382, offset 0, flags [DF], proto: TCP (6),

 length: 52) 192.168.5.23.38993 > 194.91.250.11.5190: ., cksum 0x1fd7 (correct),

 1:1(0) ack 234 win 11945 <nop,nop,timestamp 506366 2713588760>

12:57:53.401516 IP (tos 0x0, ttl  48, id 45237, offset 0, flags [DF], proto: TCP (6),

 length: 210) 194.91.250.11.443 > 192.168.5.23.56385: P 2092522043:2092522213(170)

 ack 3645927446 win 10720

...

Команда tcpdump обладает очень богатым интерфейсом, включающем условные выражения, по которым должны выделяться интересующие пакеты. Например, можно использовать условия удалённого порта (равно 80):

desktop ~ # tcpdump -i eth1 -l -n -vv dst port 80

tcpdump: listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes

13:55:36.563959 IP (tos 0x0, ttl  64, id 3936, offset 0, flags [DF], proto: TCP (6),

 length: 60) 192.168.5.23.52348 > 213.180.204.11.80: S, cksum 0x2766 (correct),

 3855548287:3855548287(0) win 5840 <mss 1460,sackOK,timestamp 1372191 0,nop,wscale 2>

13:55:36.592654 IP (tos 0x0, ttl  64, id 3937, offset 0, flags [DF], proto: TCP (6),

 length: 40) 192.168.5.23.52348 > 213.180.204.11.80: ., cksum 0xebc5 (correct),

 3855548288:3855548288(0) ack 3869420799 win 5840

13:55:36.592731 IP (tos 0x0, ttl  64, id 3938, offset 0, flags [DF], proto: TCP (6),

 length: 627) 192.168.5.23.52348 > 213.180.204.11.80: P 0:587(587) ack 1 win 5840

...

nmap 

Команда nmap – сетевой сканер, с помощью которого можно определить уязвимость удалённых хостов. Основное назначение этой программы – определение состояние портов удалённого хоста (закрыты они, открыты или заблокированны). Также программа может на основании собственной базы знаний определить по поведению удалённого хоста, какая операционная система запущена на нём.

Команды удалённого терминала

telnet 

telnet – программа сетевого терминала.

ssh 

Программа ssh является более современным и защищённым аналогом программы telnet.

Команды по управлению сетевым экраном

iptables 

Команда iptables является интерфейсом к межсетевому экрану ядра Linux.

Резюме

В этом разделе описываются основные команды по управлению и диагностике сети в UNIX.

Ключевые термины: ifconfig, arp, route, ping, traceroute, netstat, host, tcpdump, nmap, telnet, ssh, iptables


Практическое занятие. Управление сетью

Цель занятия: изучение инструментов конфигурирования сети в UNIX, включающих настроку параметров TCP/IP-сети.

Изучаемые команды: arp, ifconfig, netstat, ping, route, ssh, telnet, traceroute

Сценарий: Настройка сетевого интерфейса

Сценарий посвящен сетевым интерфейсам – прослойке между канальным и сетевым уровнем в UNIX. В сценарии показано, как получать информацию о настроенных в системе сетевых интерфейсах и как производится их диагностика и конфигурирование.

Начальные условия: Командная строка суперпользователя после входа в систему.

  1.  Получить сведения обо всех настроенных сетевых интерфейсах с помощью команды ifconfig -a:
    desktop ~ # ifconfig -a
    eth0      Link encap:Ethernet  HWaddr 00:0D:60:8D:42:AA  
             
    inet addr:192.168.1.5  Bcast:192.168.1.255  Mask:255.255.255.0
             UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
             RX packets:204779 errors:0 dropped:0 overruns:0 frame:0
             TX packets:107606 errors:0 dropped:0 overruns:0 carrier:0
             collisions:0 txqueuelen:1000
             RX bytes:302429520 (288.4 Mb)  TX bytes:9177476 (8.7 Mb)
             Base address:0x8000 Memory:c0220000-c0240000

    lo        Link encap:Local Loopback  
             
    inet addr:127.0.0.1  Mask:255.0.0.0
             UP LOOPBACK RUNNING  MTU:16436  Metric:1
             RX packets:228 errors:0 dropped:0 overruns:0 frame:0
             TX packets:228 errors:0 dropped:0 overruns:0 carrier:0
             collisions:0 txqueuelen:0
             RX bytes:17724 (17.3 Kb)  TX bytes:17724 (17.3 Kb)
  2.  Проверить возможность соединения с локальной машиной с помощью команды ping 127.0.0.1.
    desktop ~ # ping 127.0.0.1
    PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
    64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.052 ms
    64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.051 ms
    64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.055 ms
  3.  Перед конфигурированием интерфейса eth0 необходимо убедиться, что он отключен. Отключение сетевого интерфейса eth0 производится командой ifconfig eth0 down.
    desktop ~ # ifconfig eth0 down
    desktop ~ # ifconfig -a
    lo        Link encap:Local Loopback  
             
    inet addr:127.0.0.1  Mask:255.0.0.0
             UP LOOPBACK RUNNING  MTU:16436  Metric:1
             RX packets:228 errors:0 dropped:0 overruns:0 frame:0
             TX packets:228 errors:0 dropped:0 overruns:0 carrier:0
             collisions:0 txqueuelen:0
             RX bytes:17724 (17.3 Kb)  TX bytes:17724 (17.3 Kb)
  4.  Для связи сетевого интерфейса eth0 с IP-адресом 192.168.1.1 выполним команду ifconfig eth0 192.168.1.1 up.
    desktop ~ # ifconfig eth0 192.168.1.1 up
    desktop ~ # ifconfig -a
    eth0      Link encap:Ethernet  HWaddr 00:0C:F1:2E:0E:F9
             
    inet addr:192.168.1.1  Bcast:192.168.1.255  Mask:255.255.255.0
             UP BROADCAST MULTICAST  MTU:1500  Metric:1
             RX packets:0 errors:0 dropped:0 overruns:0 frame:0
             TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
             collisions:0 txqueuelen:1000
             RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)
             Interrupt:11 Base address:0x2000 Memory:c0210000-c0210fff

    lo        Link encap:Local Loopback  
             
    inet addr:127.0.0.1  Mask:255.0.0.0
             UP LOOPBACK RUNNING  MTU:16436  Metric:1
             RX packets:228 errors:0 dropped:0 overruns:0 frame:0
             TX packets:228 errors:0 dropped:0 overruns:0 carrier:0
             collisions:0 txqueuelen:0
             RX bytes:17724 (17.3 Kb)  TX bytes:17724 (17.3 Kb)
    При этом по умолчанию используется сеть класса C, т.е. маска сети «255.255.255.0».
  5.  Для задания специфической маски подсети используется параметр netmask. Например, данная команда задаёт параметры сети класса A: ifconfig eth0 10.10.1.1 netmask 255.0.0.0 up.
    desktop ~ # ifconfig eth0 10.10.1.1 netmask 255.0.0.0 up
    desktop ~ # ifconfig -a
    eth0      Link encap:Ethernet  HWaddr 00:0C:F1:2E:0E:F9
             
    inet addr:10.10.1.1  Bcast:10.255.255.255  Mask:255.0.0.0
             UP BROADCAST MULTICAST  MTU:1500  Metric:1
             RX packets:0 errors:0 dropped:0 overruns:0 frame:0
             TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
             collisions:0 txqueuelen:1000
             RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)
             Interrupt:11 Base address:0x2000 Memory:c0210000-c0210fff

    lo        Link encap:Local Loopback  
             
    inet addr:127.0.0.1  Mask:255.0.0.0
             UP LOOPBACK RUNNING  MTU:16436  Metric:1
             RX packets:228 errors:0 dropped:0 overruns:0 frame:0
             TX packets:228 errors:0 dropped:0 overruns:0 carrier:0
             collisions:0 txqueuelen:0
             RX bytes:17724 (17.3 Kb)  TX bytes:17724 (17.3 Kb)
  6.  С помощью команды arp можно узнать текущую ARP-таблицу операционной системы (соответствие MAC-адресов канального уровня IP-адресам). Таблица автоматически поддерживается операционной системой в процессе сетевого обмена.
    desktop ~ # arp
    Address                  HWtype  HWaddress           Flags Mask            Iface
    gate.localnet            ether   00:02:44:8F:16:B7   C                     eth0

Сценарий: Настройка таблицы маршрутизации

В сценарии производится изучение и настройка таблицы маршрутизации IP. С помощью специальной программы производится изучение маршрута следования пакетов.

Начальные условия: Командная строка суперпользователя, сетевой интерфейс настроен на статический IP-адрес.

  1.  Для просмотра таблицы маршрутизации воспользуемся командой route -n:
    desktop ~ # route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    10.0.0.0        0.0.0.0         255.0.0.0       U     0      0        0 eth0
    127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        0 lo
    0.0.0.0         10.10.1.254     0.0.0.0         UG    0      0        0 eth0
  2.  Без использования ключа -n для всех имён будут использоваться символьные значения: route 
    desktop ~ # route
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    localnet        *               255.0.0.0       U     0      0        0 eth0
    loopback        *               255.0.0.0       U     0      0        0 lo
    default         gate.localnet   0.0.0.0         UG    0      0        0 eth0
  3.  Для добавления новой строки в таблицу нужно воспользоваться параметром add: route add -host 10.10.2.1 dev eth0.
    desktop ~ # route add -host 10.10.2.1 dev eth0
    desktop ~ # route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    10.10.2.1       0.0.0.0         255.255.255.255 UH    0      0        0 eth0
    10.0.0.0        0.0.0.0         255.0.0.0       U     0      0        0 eth0
    127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        0 lo
    0.0.0.0         10.10.1.254     0.0.0.0         UG    0      0        0 eth0
    Эта команда добавляет явный маршрут до отдельного хоста с указанным IP-адресом через интерфейс eth0.
  4.  Аналогичным образом маршрут удаляется, используется параметр del: route del -host 10.10.2.1.
    desktop ~ # route add -host 10.10.2.1 dev eth0
    desktop ~ # route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    10.0.0.0        0.0.0.0         255.0.0.0       U     0      0        0 eth0
    127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        0 lo
    0.0.0.0         10.10.1.254     0.0.0.0         UG    0      0        0 eth0
  5.  В качестве назначения маршрута можно указывать также целую сеть (парамеметр -net. Рассмотрим команду, которая задаёт маршрут в сеть «192.168.1.0» через шлюз «10.10.1.253»: route add -net 192.168.1.0 gw 10.10.1.253.
    desktop ~ # route add -net 192.168.1.0 gw 10.10.1.253
    desktop ~ # route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    192.168.1.0     10.10.1.253     255.255.255.255 UG    0      0        0 eth0
    10.0.0.0        0.0.0.0         255.0.0.0       U     0      0        0 eth0
    127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        0 lo
    0.0.0.0         10.10.1.254     0.0.0.0         UG    0      0        0 eth0
  6.  Рассмотрим простой маршрут движения пакетов до хоста в Internet с помощью программы traceroute ya.ru:
    desktop ~ # traceroute ya.ru
    traceroute to ya.ru (213.180.204.8), 64 hops max, 40 byte packets
    1  10.10.1.254 (10.10.1.254)  3.418 ms  2.67 ms  0.719 ms
     2  cs7206.rinet.ru (195.54.192.28)  1.34 ms  1.378 ms  0.647 ms
     3  ix2-m9.yandex.net (193.232.244.93)  1.554 ms  1.457 ms  1.420 ms
     4  c3-vlan4.yandex.net (213.180.210.146)  2.137 ms  2.154 ms  1.842 ms
     5  ya.ru (213.180.204.8)  2.646 ms  2.183 ms 2.220 ms

Сценарий: Изучение службы доменных имён

Сценарий посвящен изучению службы доменных имён – её использованию и конфигурированию.

Начальные условия: Командная строка суперпользователя, сетевой интерфейс настроен на статический IP-адрес.

  1.  Просмотрим содержимое файла /etc/hosts, содержащего имена локальных хостов: cat /etc/hosts 
    desktop ~ # cat /etc/hosts
    #
    # hosts         This file describes a number of hostname-to-address
    #               mappings for the TCP/IP subsystem.  It is mostly
    #               used at boot time, when no name servers are running.
    #               On small systems, this file can be used instead of a
    #               "named" name server.  Just add the names, addresses
    #               and any aliases to this file...
    #

    # Localhost
    127.0.0.1       localhost

    # Home LAN
    10.10.1.254    gate.localnet gate
    10.10.1.20     boss.localnet boss
  2.  Проверим работоспособность DNS с помощью команды обращения к хосту в Internet по имени ping ya.ru:
    desktop ~ # ping ya.ru
    PING ya.ru (213.180.204.8) 56(84) bytes of data.
    64 bytes from ya.ru (213.180.204.8): icmp_seq=1 ttl=54 time=3.56 ms
    64 bytes from ya.ru (213.180.204.8): icmp_seq=2 ttl=54 time=2.22 ms
  3.  Для корректной работы службы доменных имен необходимо прописать используемые серверы DNS в файле /etc/resolv.conf. Просмотрим его содержимое cat /etc/resolv.conf:
    desktop ~ # cat /etc/resolv.conf
    domain localnet
    nameserver 10.10.1.17
  4.  С помощью команды host ya.ru узнаем информацию DNS о хосте в Internet:
    desktop ~ # host ya.ru
    ya.ru has address 213.180.204.8
    ya.ru mail is handled by 10 cmail.yandex.ru.
  5.  Вторым аргументом команды host ya.ru ns1.yandex.ru имя DNS-сервера, с которого необходимо получить инормацию:
    desktop ~ # host ya.ru ns1.yandex.ru
    ya.ru has address 213.180.204.8
    Using domain server:
    Name: ns1.yandex.ru
    Address: 213.180.193.1#53
    Aliases:

    Using domain server:
    Name: ns1.yandex.ru
    Address: 213.180.193.1#53
    Aliases:

    ya.ru mail is handled by 10 cmail.yandex.ru.

Сценарий: Простая диагностика работы сети

Сценарий рассматривает самые простые способы диагностики работы сети.

Начальные условия: Командная строка суперпользователя, сетевой интерфейс настроен на статический IP-адрес.

  1.  Для проверки работоспособности сетевых служб воспользуемся командой удалённого терминала: telnet ya.ru 80. В данном случае будет установлено соединение с хостом в Internet по порту 80 (HTTP):
    desktop ~ # telnet ya.ru 80
    Trying 213.180.204.8...
    Connected to ya.ru.
    Escape character is '^]'.
    GET / HTTP/1.0

    HTTP/1.0 200 OK
    Server: thttpd/2.25b 29dec2003
    Content-Type: text/html; charset=windows-1251
    Date: Wed, 23 Nov 2005 05:40:33 GMT
    Last-Modified: Mon, 07 Nov 2005 15:13:14 GMT
    Accept-Ranges: bytes
    Connection: close
    Content-Length: 2005

    <html>
    <head>
    ...
  2.  Если во время соединения с удалённым узлом ввести команду netstat -t, то можно увидеть, что состояние этого соединения – «ESTABLISHED»:
    desktop ~ # netstat -t
    Active Internet connections (servers and established)
    tcp        0      0 desktop:42639           ya.ru:http              ESTABLISHED
  3.  Информацию обо всех соединениях в системе можно получить с помощью команды netstat -a. В этом случае будет выводиться информация обо всех TCP-, UDP- и локальных сокетах:
    desktop ~ # netstat -a
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State
    tcp        0      0 *:32769                 *:*                     LISTEN
    tcp        0      0 *:32770                 *:*                     LISTEN
    tcp        0      0 *:sunrpc                *:*                     LISTEN
    tcp        0      0 *:ssh                   *:*                     LISTEN
    tcp        0      0 desktop:42639           ya.ru:http              ESTABLISHED
    udp        0      0 *:32768                 *:*
    udp        0      0 *:32769                 *:*
    udp        0      0 *:sunrpc                *:*
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node Path
    unix  2      [ ACC ]     STREAM     LISTENING     8344   /var/run/acpid.socket
    unix  2      [ ACC ]     STREAM     LISTENING     8866   /var/run/sdp
    ...

Сценарий: Работа по удалённому терминалу

Сценарий рассматривает работу по удалённому сетевому терминалу с использованием программы ssh.

Начальные условия: Командная строка суперпользователя, сетевой интерфейс настроен на статический IP-адрес.

  1.  С помощью команды ssh user@10.10.1.222 
    desktop ~ # ssh user@10.10.1.222
    Password:
    Last login: Sat Nov 21 15:56:20 2005 from 10.10.1.5
    user@remote ~ $
    user@remote ~ $ exit
  2.  Выполним команду who, чтобы убедиться, что находимся на удалённой машине. Для всех пользователей, работающий удалённо, указывается IP-адрес.
    user@remote ~ $ who
    user     vc/1         Nov 14 14:04
    user     pts/0        Nov 22 10:55 (10.10.1.5)
  3.  Для завершение сеанса удалённого терминала нужно выйти из командной оболочки с помощью команды exit.
    user@remote ~ $ exit
    logout
    Connection to 10.10.1.5 closed.
    desktop ~ #

Задания для самоподготовки

  1. Настройте сетевой интерфейс eth0 на сеть с адресом 192.168.77.0, состоящую из 16 машин.
  2. С помощью программы netstat определите какие значения принимают локальные порты в исходящих TCP-соединениях? В какой диапазон они попадают?


Сценарии практического занятия на тему: Управление сетью

«Сценарий: Настройка сетевого интерфейса»

  1. ifconfig -a
  2. ping 127.0.0.1
  3. ifconfig eth0 down
  4. ifconfig eth0 192.168.1.1 up
  5. ifconfig eth0 10.10.1.1 netmask 255.0.0.0 up
  6. arp

«Сценарий: Настройка таблицы маршрутизации»

  1. route -n
  2. route
  3. route add -host 10.10.2.1 dev eth0
  4. route del -host 10.10.2.1
  5. route add -net 192.168.1.0 gw 10.10.1.253
  6. traceroute ya.ru

«Сценарий: Изучение службы доменных имён»

  1. cat /etc/hosts
  2. ping ya.ru
  3. cat /etc/resolv.conf
  4. host ya.ru
  5. host ya.ru ns1.yandex.ru

«Сценарий: Простая диагностика работы сети»

  1. telnet ya.ru 80
  2. netstat -t
  3. netstat -a

«Сценарий: Работа по удалённому терминалу»

  1. ssh user@10.10.1.222
  2. who
  3. exit


Практическое занятие. Расширенная диагностика и настройка сети

Цель занятия: знакомство с командами анализа сетевой активности и хостов в TCP/IP-сети и интстументами управления межсетевым экраном в Linux.

Изучаемые команды: iptables, nmap, tcpdump 

Сценарий: Сканирование локальной сети

Сценарий описывает возможности по просмотру сетевого трафика, проходящего через присутствующие в системе сетевые интерфейсы.

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

  1.  Выведем все пакеты, проходящие через интерфейс eth0, с помощью команды tcpdump -i eth0 -l -n:
    desktop ~ # tcpdump -i eth0 -l -n
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
    14:37:23.929208 IP 192.168.0.204.32791 > 192.168.0.1.53:  40133+ A? ya.ru. (23)
    14:37:23.931050 IP 192.168.0.1.53 > 192.168.0.204.32791:  40133- 1/0/0 A 213.180.204.8 (39)
    14:37:23.932778 IP 192.168.0.204 > 213.180.204.8: ICMP echo request, id 12586, seq 1, length 64
    14:37:23.954412 IP 213.180.204.8 > 192.168.0.204: ICMP echo reply, id 12586, seq 1, length 64
    14:37:23.954784 IP 192.168.0.204.32791 > 192.168.0.1.53:  29723+ PTR? 8.204.180.213.in-addr.arpa. (44)
    14:37:23.956530 IP 192.168.0.1.53 > 192.168.0.204.32791:  29723- 1/0/0 (63)
    ...
    Прервать выполнение команды можно нажатием Ctrl-C.
  2.  Используем фильтр по уалённому порту для просмотра всего трафика в сети, генерируемого данной машиной, tcpdump -i eth0 -l -n src host 192.168.0.204:
    desktop ~ # tcpdump -i eth0 -l -n src host 192.168.0.204
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
    14:43:02.492487 IP 192.168.0.204.32791 > 192.168.0.1.53:  13524+ AAAA? yandex.ru. (27)
    14:43:02.520278 IP 192.168.0.204.32791 > 192.168.0.1.53:  12908+ AAAA? yandex.ru. (27)
    14:43:02.522799 IP 192.168.0.204.32791 > 192.168.0.1.53:  48501+ A? yandex.ru. (27)
    14:43:02.525069 IP 192.168.0.204.57521 > 1.0.0.0.80: S 1379873941:1379873941(0) win
      
    5840 <mss 1460,sackOK,timestamp 2563967 0,nop,wscale 2>
    ...
  3.  Условия можно объёдинять, например, посмотрим все обращения по протоколу http на хост ya.ru, tcpdump -i eth0 -l -n src host 192.168.0.204 and dst port 80:
    desktop ~ # tcpdump -i eth0 -l -n src host 192.168.0.204 and dst port 80
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
    14:49:39.015251 IP 192.168.0.204.38172 > 213.180.204.11.80: S 1789772982:1789772982(0)
      
    win 5840 <mss 1460,sackOK,timestamp 2663083 0,nop,wscale 2>
    14:49:39.054551 IP 192.168.0.204.38172 > 213.180.204.11.80: . ack 851659167 win 5840
    14:49:39.056486 IP 192.168.0.204.38172 > 213.180.204.11.80: P 0:626(626) ack 1 win 5840
    14:49:39.598337 IP 192.168.0.204.38172 > 213.180.204.11.80: . ack 1411 win 8460
    14:49:39.604713 IP 192.168.0.204.38172 > 213.180.204.11.80: . ack 2821 win 11280
    14:49:39.659811 IP 192.168.0.204.38172 > 213.180.204.11.80: . ack 4231 win 14100
    14:49:39.784351 IP 192.168.0.204.38172 > 213.180.204.11.80: . ack 5641 win 16920
    14:49:39.848824 IP 192.168.0.204.38172 > 213.180.204.11.80: . ack 7051 win 19740
    14:49:39.966120 IP 192.168.0.204.38172 > 213.180.204.11.80: F 626:626(0) ack 8118
      win 22560
    В данном примере можно увидеть одно последовательное TCP-соединение.
  4.  С помощью параметров -X и -s можно просмотреть содержимое пакетов, tcpdump -i eth0 -l -n -X -s 2048 src host 192.168.0.204 and dst port 80:
    desktop ~ # tcpdump -i eth0 -l -n -X -s 2048 src host 192.168.0.204 and dst port 80
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 2048 bytes
    15:00:20.150901 IP 192.168.0.204.58273 > 213.180.204.11.80: S 2473175603:2473175603(0)
      
    win 5840 <mss 1460,sackOK,timestamp 2823357 0,nop,wscale 2>
           0x0000:  4500 003c be8e 4000 4006 18f9 c0a8 00cc  E..<..@.@.......
           0x0010:  d5b4 cc0b e3a1 0050 9369 aa33 0000 0000  .......P.i.3....
           0x0020:  a002 16d0 9789 0000 0204 05b4 0402 080a  ................
           0x0030:  002b 14bd 0000 0000 0103 0302            .+..........
    15:00:20.172752 IP 192.168.0.204.58273 > 213.180.204.11.80: . ack 2349139833 win 5840
           
    0x0000:  4500 0028 be8f 4000 4006 190c c0a8 00cc  E..(..@.@.......
           0x0010:  d5b4 cc0b e3a1 0050 9369 aa34 8c05 0779  .......P.i.4...y
           0x0020:  5010 16d0 80c1 0000                      P.......
    15:00:20.172815 IP 192.168.0.204.58273 > 213.180.204.11.80: P 0:624(624) ack 1 win 5840
           
    0x0000:  4500 0298 be90 4000 4006 169b c0a8 00cc  E.....@.@.......
           0x0010:  d5b4 cc0b e3a1 0050 9369 aa34 8c05 0779  .......P.i.4...y
           0x0020:  5018 16d0 023e 0000 4745 5420 2f79 616e  P....>..GET./yan
           0x0030:  6473 6561 7263 683f 7465 7874 3d74 6573  dsearch?text=tes
           0x0040:  7426 7374 7970 653d 7777 7720 4854 5450  t&stype=www.HTTP
           0x0050:  2f31 2e31 0d0a 486f 7374 3a20 7777 772e  /1.1..Host:.www.
           0x0060:  7961 6e64 6578 2e72 750d 0a55 7365 722d  yandex.ru..User-
           0x0070:  4167 656e 743a 204d 6f7a 696c 6c61 2f35  Agent:.Mozilla/5
           0x0080:  2e30 2028 5831 313b 2055 3b20 4c69 6e75  .0.(X11;.U;.Linu
           0x0090:  7820 6936 3836 3b20 656e 2d55 533b 2072  x.i686;.en-US;.r
           0x00a0:  763a 312e 372e 3132 2920 4765 636b 6f2f  v:1.7.12).Gecko/
    ...

Сценарий: Сканирование удалённых хостов

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

  1.  Простое сканирование хоста можно произвести, если указать его IP-адрес в аргументе команды nmap: nmap 192.168.0.120:
    user@desktop ~ $ nmap 192.168.0.120
    Starting nmap 3.75 ( http://www.insecure.org/nmap/ ) at 2006-02-06 23:21 MSK
    Interesting ports on gate.localnet (192.168.0.120):
    (The 1658 ports scanned but not shown below are in state: closed)
    PORT    STATE SERVICE
    21/tcp  open  ftp
    22/tcp  open  ssh
    80/tcp  open  http
    443/tcp open  https
    873/tcp open  rsync

    Nmap run completed -- 1 IP address (1 host up) scanned in 0.555 seconds
    При этом nmap производит попытки соединения с портами, что может быть обнаружено на стороне хоста.
  2.  Более «тихим» способом сканирования является SYN-сканирование, когда вместо настоящего соединения посылается только SYN-пакет. Этот способ сканировани доступен только суперпользователю: nmap -sS 192.168.0.120 
    desktop ~ # nmap -sS 192.168.0.120
    Starting nmap 3.75 ( http://www.insecure.org/nmap/ ) at 2006-02-06 23:28 MSK
    Interesting ports on gate.localnet (192.168.0.120):
    (The 1658 ports scanned but not shown below are in state: closed)
    PORT    STATE SERVICE
    21/tcp  open  ftp
    22/tcp  open  ssh
    80/tcp  open  http
    443/tcp open  https
    873/tcp open  rsync
    MAC Address: 00:11:2F:F4:4B:55 (Asustek Computer)

    Nmap run completed -- 1 IP address (1 host up) scanned in 0.405 seconds
  3.  Самую полную информацию о тестируемом хосте можно узнать, если использовать ключи -v (дважды) и -O, nmap -sS -v -v -O 192.168.0.120:
    desktop ~ # nmap -sS -v -v -O 192.168.0.120
    Starting nmap 3.75 ( http://www.insecure.org/nmap/ ) at 2006-02-06 23:31 MSK
    Initiating SYN Stealth Scan against gate.localnet (192.168.0.120) [1663 ports] at 23:31
    Discovered open port 80/tcp on 192.168.0.120
    Discovered open port 21/tcp on 192.168.0.120
    Discovered open port 443/tcp on 192.168.0.120
    Discovered open port 22/tcp on 192.168.0.120
    Discovered open port 873/tcp on 192.168.0.120
    The SYN Stealth Scan took 0.08s to scan 1663 total ports.
    For OSScan assuming port 21 is open, 1 is closed, and neither are firewalled
    Host gate.localnet (192.168.0.120) appears to be up ... good.
    Interesting ports on gate.localnet (192.168.0.120):
    (The 1658 ports scanned but not shown below are in state: closed)
    PORT    STATE SERVICE
    21/tcp  open  ftp
    22/tcp  open  ssh
    80/tcp  open  http
    443/tcp open  https
    873/tcp open  rsync
    MAC Address: 00:11:2F:F4:4B:55 (Asustek Computer)
    Device type: general purpose
    Running: Linux 2.4.X|2.5.X|2.6.X
    OS details: Linux 2.4.18 - 2.6.7
    OS Fingerprint:
    TSeq(Class=RI%gcd=1%SI=2E7845%IPID=Z%TS=1000HZ)
    T1(Resp=Y%DF=Y%W=16A0%ACK=S++%Flags=AS%Ops=MNNTNW)
    T2(Resp=N)
    T3(Resp=Y%DF=Y%W=16A0%ACK=S++%Flags=AS%Ops=MNNTNW)
    T4(Resp=Y%DF=Y%W=0%ACK=O%Flags=R%Ops=)
    T5(Resp=Y%DF=Y%W=0%ACK=S++%Flags=AR%Ops=)
    T6(Resp=Y%DF=Y%W=0%ACK=O%Flags=R%Ops=)
    T7(Resp=Y%DF=Y%W=0%ACK=S++%Flags=AR%Ops=)
    PU(Resp=Y%DF=N%TOS=C0%IPLEN=164%RIPTL=148%RID=E%RIPCK=E%UCK=E%ULEN=134%DAT=E)

    Uptime 24.662 days (since Fri Jan 13 07:38:30 2006)
    TCP Sequence Prediction: Class=random positive increments
                            
    Difficulty=3045445 (Good luck!)
    TCP ISN Seq. Numbers: FC47E005 FC966A63 FC61563D FBC11ADD FC5E53A5 FBBE2AA2
    IPID Sequence Generation: All zeros

    Nmap run completed -- 1 IP address (1 host up) scanned in 2.340 seconds
    На основании анализа поведения системы программа делает предположение о типе операционной системы, запущенной на хосте.

Сценарий: Настройка сетевого экрана

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

  1.  Для просмотра текущей таблицы правил сетевого экрана воспользуемся командой iptables -L:
    desktop ~ # iptables -L
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination

    Chain FORWARD (policy ACCEPT)
    target     prot opt source               destination

    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
    Все три цепочки не содержат правил и имеют политику разрешения всех пакетов по умолчанию.
  2.  Запретим весь сетевой трафик как на вход, так и на выход командами iptables -P INPUT DROP, iptables -P OUTPUT DROP:
    desktop ~ # iptables -P INPUT DROP
    desktop ~ # iptables -P OUTPUT DROP
    desktop ~ # iptables -L
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination

    Chain FORWARD (policy ACCEPT)
    target     prot opt source               destination

    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
  3.  Любой сетевой обмен (даже команда ping 127.0.0.1) будет запрещён:
    desktop ~ # ping 127.0.0.1
    PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
    ping: sendmsg: Operation not permitted
    ping: sendmsg: Operation not permitted
  4.  Разрешим вход и выход всем пакетам, связанным с локальным интерфейсом (lo) с помощью команд: iptables -A INPUT -i lo -j ACCEPT и iptables -A OUTPUT -o lo -j ACCEPT.
    desktop ~ # iptables -A INPUT -i lo -j ACCEPT
    desktop ~ # iptables -A OUTPUT -o lo -j ACCEPT
    desktop ~ # ping 127.0.0.1
    PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
    64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.056 ms
    64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.043 ms
  5.  Разрешим вход и выход всем пакетам, относящимся к протоколу ICMP: iptables -A INPUT -p icmp -j ACCEPT и iptables -A OUTPUT -p icmp -j ACCEPT.
    desktop ~ # iptables -A INPUT -p icmp -j ACCEPT
    desktop ~ # iptables -A OUTPUT -p icmp -j ACCEPT
  6.  Чтобы пропускать пакеты, относящиеся ко всем установленным соединениям, добавим два правила: iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT и iptables -A OUTPUT -m state --state ESTABLISHED -j ACCEPT 
    desktop ~ # iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT
    desktop ~ # iptables -A OUTPUT -m state --state ESTABLISHED -j ACCEPT
  7.  Теперь осталось только разрешить входящие и исходящие соединения на определённые порты. Например, для исходящего доступа по протоколу HTTP выполним iptables -A OUTPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT:
    desktop ~ # iptables -A OUTPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT
  8.  В завершение можно очистить все заданные правила с помощью iptables -F:
    desktop ~ # iptables -F
    desktop ~ # iptables -L
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination

    Chain FORWARD (policy ACCEPT)
    target     prot opt source               destination

    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
    Обратите внимание, что это не изменило политики по умолчанию.

Задания для самоподготовки

  1. Подсчитайте число устанавливаемых tcp соеднинений в сети за одну минуту.
  2. Проследите сожержимое tcp-сессии telnet- и ssh-соединения. Сравните результаты.


Сценарии практического занятия на тему: Расширенная диагностика и настройка сети

«Сценарий: Сканирование локальной сети»

  1. tcpdump -i eth0 -l -n
  2. tcpdump -i eth0 -l -n src host 192.168.0.204
  3. tcpdump -i eth0 -l -n src host 192.168.0.204 and dst port 80
  4. tcpdump -i eth0 -l -n -X -s 2048 src host 192.168.0.204 and dst port 80

«Сценарий: Сканирование удалённых хостов»

  1. nmap 192.168.0.120
  2. nmap -sS 192.168.0.120
  3. nmap -sS -v -v -O 192.168.0.120

«Сценарий: Настройка сетевого экрана»

  1. iptables -L
  2. iptables -P INPUT DROP
  3. iptables -P OUTPUT DROP
  4. ping 127.0.0.1
  5. iptables -A INPUT -i lo -j ACCEPT
  6. iptables -A OUTPUT -o lo -j ACCEPT
  7. iptables -A INPUT -p icmp -j ACCEPT
  8. iptables -A OUTPUT -p icmp -j ACCEPT
  9. iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT
  10. iptables -A OUTPUT -m state --state ESTABLISHED -j ACCEPT
  11. iptables -A OUTPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT
  12. iptables -F


Примеры прикладных программы сетевого обмена

sendmail: программа отправления почты

Программа sendmail является частью одноимённого SMTP-сервера и позволяет отправлять письма электронной почты. Однако, многие другие распространённые почтовые серверы или (например, Postfix или ssmtp) программы предоставляют совместимый с sendmail интерфейс.

По умолчанию sendmail принимает на стандартный ввод тело письма вплоть до получения EOF. Таким образом, при работе в командной строке для завершения ввода текста необходимо нажать Ctrl-D.

Для простейшего использования программы достаточно ключа -t. При этом программа sendmail проанализирует заголовок письма и выберет из него имена и адреса отправителя и получателя письма.

Пример отправления простого письма:

Пример. Отправка письма с помощью sendmail

user@desktop ~ $ sendmail -t

From: Vasily Poopkin <vas@pupkin.ru>

To: Aleksey Fedoseev <aleksey@fedoseev.net>

Subject: Idea

Hi there!

I'm Vasily, and you're Aleksey.

Super-puper email body.

^D

user@desktop ~ $


Заголовки отделаются от тела письма пустой строкой в соответствии в RFC протокола SMTP.

Параметры команды

Также распространены следующие параметры:

-F строка 

задать полное имя отправителя, этот параметр игнорируется, если в письме был указан заголовок «From:»;

-f адрес или -r адрес 

использовать параметр в качестве адреса отправителя, используемого в обмене с SMTP-сервером (иногда его называют «envelope from»);

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

user@desktop ~ $ sendmail -f user@desktop.localnet alice@mails.org bob@mails.org test@fedoseev.net

From: User <user@desktop.localnet>

hello!

This is broadcats message.

^D

Команду sendmail можно с успехом использовать при автоматической генерации почтовых сообщений администратору, если на стандартный ввод команды sendmail подать вывод команды cat или echo:

user@desktop ~ $ cat warning.txt | sendmail admin@localhost

wget: консольный загрузчик файлов

wget – универсальная программа закачки файлов по протоколам HTTP, HTTPS и FTP. При работе с HTML/XML-файлами программа может находить ссылки и переходить по ним, получая полноценную локальную версию сайта.

Команда имеет простой формат:

wget [опции] URL

Например, для загрузки файла достаточно выполнить команду:

user@desktop ~ $ wget ftp://mirror.aiya.ru/pub/ALTLinux/ISO/MD5SUM

--12:27:19--  ftp://mirror.aiya.ru/pub/ALTLinux/ISO/MD5SUM

          => `MD5SUM'

Распознаётся mirror.aiya.ru... 85.142.20.147

Устанавливается соединение с mirror.aiya.ru|85.142.20.147|:21... соединение установлено.

Выполняется вход под именем anonymous ... Выполнен вход в систему!

==> SYST ... готово.  ==> PWD ... готово.

==> TYPE I ... готово.   ==> CWD /pub/ALTLinux/ISO ... готово.

==> PASV ... готово.  ==> RETR MD5SUM ... готово.

Длина: 393 (не достоверно)

100%[=======================================================================>] 393 --.--K/s

12:27:29 (429.29 KB/s) - `MD5SUM' сохранён [393]

Загруженный файл сохраняется в локальной директории.

Параметры команды

Команда wget имеет набор параметров:

-b 

запустить программу на заднем фоне, вывод при этом перенаправляется в журнал wget;

-q 

не выводить информацию на стандартный вывод, удобно для применения в скриптах;

-t число 

число попыток загрузки файла;

-c 

продолжить закачку для файлов, загруженных частично;

-r 

загружать рекурсивно – удобно применять к директориям или к сайтам, которые необходимо загрузить целиком.

Команда обладает множеством параметров, связанными как с процессом закачки, так и со спецификой протоколов. Все они подробно описаны в man 1 wget.

Настройка

Команда wget имеет немало настроек, касающихся параметров доступа к хостам, использование проски-серверов и т.п.. Все они находятся в файле /etc/wget/wgetrc или в файле .wgetrc в домашней директории пользователя.


Использование Socket API для создания сетевых приложений

Socket API был впервые реализован в операционной системе Berkley UNIX. Сейчас этот программный интерфейс доступен практически в любой модификации Unix, в том числе в Linux. Хотя все реализации чем-то отличаются друг от друга, основной набор функций в них совпадает. Изначально сокеты использовались в программах на C/C++, но в настоящее время средства для работы с ними предоставляют многие языки (Python, Java и др.).

Сокеты предоставляют весьма мощный и гибкий механизм межпроцессного взаимодействия (IPC). Они могут использоваться для организации взаимодействия программ на одном компьютере, по локальной сети или через Internet, что позволяет вам создавать распределённые приложения различной сложности. Кроме того, с их помощью можно организовать взаимодействие с программами, работающими под управлением других операционных систем. Например, под Windows существует интерфейс Window Sockets, спроектированный на основе socket API.

Сокеты поддерживают многие стандартные сетевые протоколы (конкретный их список зависит от реализации) и предоставляют унифицированный интерфейс для работы с ними. Наиболее часто сокеты используются для работы в IP-сетях. В этом случае их можно использовать для взаимодействия приложений не только по специально разработанным, но и по стандартным протоколам - HTTP, FTP, Telnet и т. д. Например, вы можете написать собственный Web-броузер или Web-сервер, способный обслуживать одновременно множество клиентов.

Понятие сокета

Сокет (socket) - это конечная точка сетевых коммуникаций. Он является чем-то вроде "портала", через которое можно отправлять байты во внешний мир. Приложение просто пишет данные в сокет; их дальнейшая буферизация, отправка и транспортировка осуществляется используемым стеком протоколов и сетевой аппаратурой. Чтение данных из сокета происходит аналогичным образом.

В программе сокет идентифицируется дескриптором - это просто переменная типа int. Программа получает дескриптор от операционной системы при создании сокета, а затем передаёт его сервисам socket API для указания сокета, над которым необходимо выполнить то или иное действие.

Атрибуты сокета

С каждым сокет связываются три атрибута: домен, тип и протокол. Эти атрибуты задаются при создании сокета и остаются неизменными на протяжении всего времени его существования. Для создания сокета используется функция socket, имеющая следующий прототип.

#include <sys/types.h>

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

Домен определяет пространство адресов, в котором располагается сокет, и множество протоколов, которые используются для передачи данных. Чаще других используются домены Unix и Internet, задаваемые константами AF_UNIX и AF_INET соответственно (префикс AF означает "address family" - "семейство адресов"). При задании AF_UNIX для передачи данных используется файловая система ввода/вывода Unix. В этом случае сокеты используются для межпроцессного взаимодействия на одном компьютере и не годятся для работы по сети. Константа AF_INET соответствует Internet-домену. Сокеты, размещённые в этом домене, могут использоваться для работы в любой IP-сети. Существуют и другие домены (AF_IPX для протоколов Novell, AF_INET6 для новой модификации протокола IP - IPv6 и т. д.), но в дальнейшем мы не будем их рассматривать.

Тип сокета определяет способ передачи данных по сети. Чаще других применяются:

  •  SOCK_STREAM. Передача потока данных с предварительной установкой соединения. Обеспечивается надёжный канал передачи данных, при котором фрагменты отправленного блока не теряются, не переупорядочиваются и не дублируются. Поскольку этот тип сокетов является самым распространённым, до конца раздела мы будем говорить только о нём. Остальным типам будут посвящены отдельные разделы.
  •  SOCK_DGRAM. Передача данных в виде отдельных сообщений (датаграмм). Предварительная установка соединения не требуется. Обмен данными происходит быстрее, но является ненадёжным: сообщения могут теряться в пути, дублироваться и переупорядочиваться. Допускается передача сообщения нескольким получателям (multicasting) и широковещательная передача (broadcasting).
  •  SOCK_RAW. Этот тип присваивается низкоуровневым (т. н. "сырым") сокетам. Их отличие от обычных сокетов состоит в том, что с их помощью программа может взять на себя формирование некоторых заголовков, добавляемых к сообщению.

Обратите внимание, что не все домены допускают задание произвольного типа сокета. Например, совместно с доменом Unix используется только тип SOCK_STREAM. С другой стороны, для Internet-домена можно задавать любой из перечисленных типов. В этом случае для реализации SOCK_STREAM используется протокол TCP, для реализации SOCK_DGRAM - протокол UDP, а тип SOCK_RAW используется для низкоуровневой работы с протоколами IP, ICMP и т. д.

Наконец, последний атрибут определяет протокол, используемый для передачи данных. Как мы только что видели, часто протокол однозначно определяется по домену и типу сокета. В этом случае в качестве третьего параметра функции socket можно передать 0, что соответствует протоколу по умолчанию. Тем не менее, иногда (например, при работе с низкоуровневыми сокетами) требуется задать протокол явно. Числовые идентификаторы протоколов зависят от выбранного домена; их можно найти в документации.

Адреса

Прежде чем передавать данные через сокет, его необходимо связать с адресом в выбранном домене (эту процедуру называют именованием сокета). Иногда связывание осуществляется неявно (внутри функций connect и accept), но выполнять его необходимо во всех случаях. Вид адреса зависит от выбранного вами домена. В Unix-домене это текстовая строка - имя файла, через который происходит обмен данными. В Internet-домене адрес задаётся комбинацией IP-адреса и 16-битного номера порта. IP-адрес определяет хост в сети, а порт - конкретный сокет на этом хосте. Протоколы TCP и UDP используют различные пространства портов.

Для явного связывания сокета с некоторым адресом используется функция bind. Её прототип имеет вид:

#include <sys/types.h>

#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *addr, int addrlen);

В качестве первого параметра передаётся дескриптор сокета, который мы хотим привязать к заданному адресу. Второй параметр, addr, содержит указатель на структуру с адресом, а третий - длину этой структуры. Посмотрим, что она собой представляет.

struct sockaddr {

   unsigned short    sa_family;    // Семейство адресов, AF_xxx

   char              sa_data[14];  // 14 байтов для хранения адреса

};

Поле sa_family содержит идентификатор домена, тот же, что и первый параметр функции socket. В зависимости от значения этого поля по-разному интерпретируется содержимое массива sa_data. Разумеется, работать с этим массивом напрямую не очень удобно, поэтому вы можете использовать вместо sockaddr одну из альтернативных структур вида sockaddr_XX (XX - суффикс, обозначающий домен: "un" - Unix, "in" - Internet и т. д.). При передаче в функцию bind указатель на эту структуру приводится к указателю на sockaddr. Рассмотрим для примера структуру sockaddr_in.

struct sockaddr_in {

   short int          sin_family;  // Семейство адресов

   unsigned short int sin_port;    // Номер порта

   struct in_addr     sin_addr;    // IP-адрес

   unsigned char      sin_zero[8]; // "Дополнение" до размера структуры sockaddr

};

Здесь поле sin_family соответствует полю sa_family в sockaddr, в sin_port записывается номер порта, а в sin_addr - IP-адрес хоста. Поле sin_addr само является структурой, которая имеет вид:

struct in_addr {

   unsigned long s_addr;

};

Зачем понадобилось заключать всего одно поле в структуру? Дело в том, что раньше in_addr представляла собой объединение (union), содержащее гораздо большее число полей. Сейчас, когда в ней осталось всего одно поле, она продолжает использоваться для обратной совместимости.

И ещё одно важное замечание. Существует два порядка хранения байтов в слове и двойном слове. Один из них называется порядком хоста (host byte order), другой - сетевым порядком (network byte order) хранения байтов. При указании IP-адреса и номера порта необходимо преобразовать число из порядка хоста в сетевой. Для этого используются функции htons (Host TO Network Short) и htonl (Host TO Network Long). Обратное преобразование выполняют функции ntohs и ntohl.

ПРИМЕЧАНИЕ
На некоторых машинных архитектурах (к x86 PC это не относится) порядок хоста и сетевой порядок хранения байтов совпадают. Тем не менее, функции преобразования лучше применять и там, поскольку это улучшит переносимость программы. Это никак не скажется на производительности, так как препроцессор сам уберёт все "лишние" вызовы этих функций, оставив их только там, где преобразование действительно необходимо.

Установка соединения (сервер)

Установка соединения на стороне сервера состоит из четырёх этапов, ни один из которых не может быть опущен. Сначала сокет создаётся и привязывается к локальному адресу. Если компьютер имеет несколько сетевых интерфейсов с различными IP-адресами, вы можете принимать соединения только с одного из них, передав его адрес функции bind. Если же вы готовы соединяться с клиентами через любой интерфейс, задайте в качестве адреса константу INADDR_ANY. Что касается номера порта, вы можете задать конкретный номер или 0 (в этом случае система сама выберет произвольный неиспользуемый в данный момент номер порта).

На следующем шаге создаётся очередь запросов на соединение. При этом сокет переводится в режим ожидания запросов со стороны клиентов. Всё это выполняет функция listen.

int listen(int sockfd, int backlog);

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

#include <sys/socket.h>

int accept(int sockfd, void *addr, int *addrlen);

Функция accept создаёт для общения с клиентом новый сокет и возвращает его дескриптор. Параметр sockfd задаёт слушающий сокет. После вызова он остаётся в слушающем состоянии и может принимать другие соединения. В структуру, на которую ссылается addr, записывается адрес сокета клиента, который установил соединение с сервером. В переменную, адресуемую указателем addrlen, изначально записывается размер структуры; функция accept записывает туда длину, которая реально была использована. Если вас не интересует адрес клиента, вы можете просто передать NULL в качестве второго и третьего параметров.

Обратите внимание, что полученный от accept новый сокет связан с тем же самым адресом, что и слушающий сокет. Сначала это может показаться странным. Но дело в том, что адрес TCP-сокета не обязан быть уникальным в Internet-домене. Уникальными должны быть только соединения, для идентификации которых используются два адреса сокетов, между которыми происходит обмен данными.

Установка соединения (клиент)

На стороне клиента для установления соединения используется функция connect, которая имеет следующий прототип.

#include <sys/types.h>

#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

Здесь sockfd - сокет, который будет использоваться для обмена данными с сервером, serv_addr содержит указатель на структуру с адресом сервера, а addrlen - длину этой структуры. Обычно сокет не требуется предварительно привязывать к локальному адресу, так как функция connect сделает это за вас, подобрав подходящий свободный порт. Вы можете принудительно назначить клиентскому сокету некоторый номер порта, используя bind перед вызовом connect. Делать это следует в случае, когда сервер соединяется с только с клиентами, использующими определённый порт (примерами таких серверов являются rlogind и rshd). В остальных случаях проще и надёжнее предоставить системе выбрать порт за вас.

Обмен данными

После того как соединение установлено, можно начинать обмен данными. Для этого используются функции send и recv. В Unix для работы с сокетами можно использовать также файловые системные вызовы read и write.

Функция send используется для отправки данных и имеет следующий прототип.

int send(int sockfd, const void *msg, int len, int flags);

Здесь sockfd - это, как всегда, дескриптор сокета, через который мы отправляем данные, msg - указатель на буфер с данными, len - длина буфера в байтах, а flags - набор битовых флагов, управляющих работой функции (если флаги не используются, передайте функции 0). Вот некоторые из них (полный список можно найти в документации):

  •  MSG_OOB. Предписывает отправить данные как срочные (out of band data, OOB). Концепция срочных данных позволяет иметь два параллельных канала данных в одном соединении. Иногда это бывает удобно. Например, Telnet использует срочные данные для передачи команд типа Ctrl+C. В настоящее время использовать их не рекомендуется из-за проблем с совместимостью (существует два разных стандарта их использования, описанные в RFC793 и RFC1122). Безопаснее просто создать для срочных данных отдельное соединение.
  •  MSG_DONTROUTE. Запрещает маршрутизацию пакетов. Нижележащие транспортные слои могут проигнорировать этот флаг.

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

int sendall(int s, char *buf, int len, int flags)

{

   int total = 0;

   int n;

   while(total < len)

   {

       n = send(s, buf+total, len-total, flags);

       if(n == -1) { break; }

       total += n;

   }

   return (n==-1 ? -1 : total);

}

Использование sendall ничем не отличается от использования send, но она отправляет весь буфер с данными целиком.

Для чтения данных из сокета используется функция recv.

int recv(int sockfd, void *buf, int len, int flags);

В целом её использование аналогично send. Она точно так же принимает дескриптор сокета, указатель на буфер и набор флагов. Флаг MSG_OOB используется для приёма срочных данных, а MSG_PEEK позволяет "подсмотреть" данные, полученные от удалённого хоста, не удаляя их из системного буфера (это означает, что при следующем обращении к recv вы получите те же самые данные). Полный список флагов можно найти в документации. По аналогии с send функция recv возвращает количество прочитанных байтов, которое может быть меньше размера буфера. Вы без труда сможете написать собственную функцию recvall, заполняющую буфер целиком. Существует ещё один особый случай, при котором recv возвращает 0. Это означает, что соединение было разорвано.

Закрытие сокета

Закончив обмен данными, закройте сокет с помощью функции close. Это приведёт к разрыву соединения.

#include <unistd.h>

int close(int fd);

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

int shutdown(int sockfd, int how);

Параметр how может принимать одно из следующих значений:

  • 0 - запретить чтение из сокета
  • 1 - запретить запись в сокет
  • 2 - запретить и то и другое

Хотя после вызова shutdown с параметром how, равным 2, вы больше не сможете использовать сокет для обмена данными, вам всё равно потребуется вызвать close, чтобы освободить связанные с ним системные ресурсы.

Обработка ошибок

Как правило, если что-то пошло не так, все рассмотренные функции возвращают -1, записывая в глобальную переменную errno код ошибки. Соответственно, вы можете проанализировать значение этой переменной и предпринять действия по восстановлению нормальной работы программы, не прерывая её выполнения. А можете просто выдать диагностическое сообщение (для этого удобно использовать функцию perror), а затем завершить программу с помощью exit.

Отладка программ

Для отладки сетевой программы не обязательно иметь настроенное сетевое подключение. Достаточно запустить клиента и сервера на одной машине, а затем использовать для соединения адрес интерфейса внутренней петли (loopback interface). В программе ему соответствует константа INADDR_LOOPBACK (не забудьте применять к ней функцию htonl!). Пакеты, направляемые по этому адресу, в сеть не попадают. Вместо этого они передаются стеку протоколов TCP/IP как только что принятые. Таким образом моделируется наличие виртуальной сети, в которой вы можете отлаживать ваши сетевые приложения.

Обмен датаграммами

В большинстве случаев надёжность передачи критична для приложения, и вместо изобретения собственного надёжного протокола поверх UDP программисты предпочитают использовать TCP. Тем не менее, иногда датаграммы оказываются полезны. Например, их удобно использовать для трансляции звука или видео по сети в реальном времени, особенно при широковещательном трансляции.

Поскольку для обмена датаграммами не нужно устанавливать соединение, использовать их гораздо проще. Создав сокет с помощью socket и bind, вы можете тут же использовать его для отправки или получения данных. Для этого вам понадобятся функции sendto и recvfrom.

int sendto(int sockfd, const void *msg, int len, unsigned int flags,

              const struct sockaddr *to, int tolen);

int recvfrom(int sockfd, void *buf, int len, unsigned int flags,

                struct sockaddr *from, int *fromlen);

Функция sendto очень похожа на send. Два дополнительных параметра to и tolen используются для указания адреса получателя. Для задания адреса используется структура sockaddr, как и в случае с функцией connect. Функция recvfrom работает аналогично recv. Получив очередное сообщение, она записывает его адрес в структуру, на которую ссылается from, а записанное количество байт - в переменную, адресуемую указателем fromlen. Как мы знаем, аналогичным образом работает функция accept.

Некоторую путаницу вносят присоединённые датаграммные сокеты (connected datagram sockets). Дело в том, что для сокета с типом SOCK_DGRAM тоже можно вызвать функцию connect, а затем использовать send и recv для обмена данными. Нужно понимать, что никакого соединения при этом не устанавливается. Операционная система просто запоминает адрес, который вы передали функции connect, а затем использует его при отправке данных. Обратите внимание, что присоединённый сокет может получать данные только от сокета, с которым он соединён.

Функции для работы с адресами и DNS

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

IP-адреса принято записывать в виде четырёх чисел, разделённых точками. Для преобразования адреса, записанного в таком формате, в число и наоборот используется семейство функций inet_addr, inet_aton и inet_ntoa.

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *in_p);

unsigned long int inet_addr(const char *cp);

char *inet_ntoa(struct in_addr in);

Функция inet_addr часто используется в программах. Она принимает строку и возвращает адрес (уже с сетевым порядком следования байтов). Проблема с этой функцией состоит в том, что значение -1, возвращаемое ею в случае ошибки, является в то же время корректным адресом 255.255.255.255 (широковещательный адрес). Вот почему сейчас рекомендуется использовать более новую функцию inet_aton (Ascii TO Network). Для обратного преобразования используется функция inet_ntoa (Network TO Ascii). Обе эти функции работают с адресами в сетевом формате. Обратите внимание, что в случае ошибки они возвращают 0, а не -1.

Для преобразования доменного имени в IP-адрес используется функция gethostbyname.

#include <netdb.h>

   

struct hostent *gethostbyname(const char *name);

Эта функция получает имя хоста и возвращает указатель на структуру с его описанием. Рассмотрим эту структуру более подробно.

struct hostent {

   char    *h_name;

   char    **h_aliases;

   int     h_addrtype;

   int     h_length;

   char    **h_addr_list;

};

#define h_addr h_addr_list[0]

  •  h_name. Имя хоста.
  •  h_aliases. Массив строк, содержащих псевдонимы хоста. Завершается значением NULL.
  •  h_addrtype. Тип адреса. Для Internet-домена - AF_INET.
  •  h_length. Длина адреса в байтах.
  •  h_addr_list. Массив, содержащий адреса всех сетевых интерфейсов хоста. Завершается нулём. Обратите внимание, что байты каждого адреса хранятся с сетевым порядке, поэтому htonl вызывать не нужно.

Как видим, gethostbyname возвращает достаточно полную информацию. Если нас интересует адрес хоста, мы можем выбрать его из массива h_addr_list. Часто берут самый первый адрес (как мы видели выше, для ссылки на него определён специальный макрос h_addr). Для определения имени хоста по адресу используется функция gethostbyaddr. Вместо строки она получает адрес (в виде sockaddr) и возвращает указатель на ту же самую структуру hostent. Используя эти две функции, нужно помнить, что они сообщают об ошибке не так, как остальные: вместо указателя возвращается NULL, а расширенный код ошибки записывается в глобальную переменную h_errno (а не errno). Соответственно, для вывода диагностического сообщения следует использовать herror вместо perror.

ПРЕДУПРЕЖДЕНИЕ
Следует иметь в виду, что функции gethostbyname и gethostbyaddr возвращают указатель на статическую область памяти. Это означает, что каждое новое обращение к одной из этих функций приведёт к перезаписи данных, полученных при преыдущем обращении.

В заключение рассмотрим ещё одно семейство полезных функций - gethostname, getsockname и getpeername.

#include <unistd.h>

int gethostname(char *hostname, size_t size);

Функция gethostname используется для получения имени локального хоста. Далее его можно преобразовать в адрес при помощи gethostbyname. Это даёт нам способ в любой момент программно получить адрес машины, на которой выполняется наша программа, что может быть полезным во многих случаях.

#include <sys/socket.h>

int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);

Функция getpeername позволяет в любой момент узнать адрес сокета на "другом конце" соединения. Она получает дескриптор сокета, соединённого с удалённым хостом, и записывает адрес этого хоста в структуру, на которую указывает addr. Фактическое количество записанных байт помещается по адресу addrlen (не забудьте записать туда размер структуры addr до вызова getpeername). Полученный адрес при необходимости можно преобразовать в строку, используя inet_ntoa или gethostbyaddr. Функция getsockname по назначению обратна getpeername и позволяет определить адрес сокета на "нашем конце" соединения.

Параллельное обслуживание клиентов

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

Обслуживание клиентских соединений в параллельных процессах

Этот способ подразумевает создание дочернего процесса для обслуживания каждого нового клиента. При этом родительский процесс занимается только прослушиванием порта и приёмом соединений. Чтобы добиться такого поведения, сразу после accept сервер вызывает системный вызов fork для создания дочернего процесса. Далее анализируется значение, которое вернула эта функция. В родительском процессе оно содержит идентификатор дочернего, а в дочернем процессе равно нулю. Используя этот признак, переходим к очередному вызову accept в родительском процессе, а дочерний процесс обслуживает клиента и завершается (_exit).

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

Мультиплексирование ввода/вывода с помощью функции select

Этот способ основан на использовании неблокирующих сокетов (nonblocking sockets) и функции select. Сначала разберёмся, что такое неблокирующие сокеты. Сокеты, которые мы до сих пор использовали, являлись блокирующими (blocking). Это название означает, что на время выполнения операции с таким сокетом ваша программа блокируется. Например, если вы вызвали recv, а данных на вашем конце соединения нет, то в ожидании их прихода ваша программа «засыпает». Аналогичная ситуация наблюдается, когда вы вызываете accept, а очередь запросов на соединение пуста. Это поведение можно изменить, используя функцию fcntl.

#include <unistd.h>

#include <fcntl.h>

.

.

sockfd = socket(AF_INET, SOCK_STREAM, 0);

fcntl(sockfd, F_SETFL, O_NONBLOCK);

.

.

Эта несложная операция превращает сокет в неблокирующий. Вызов любой функции с таким сокетом будет возвращать управление немедленно. Причём если затребованная операция не была выполнена до конца, функция вернёт -1 и запишет в errno значение EWOULDBLOCK. Чтобы дождаться завершения операции, мы можем опрашивать все наши сокеты в цикле, пока какая-то функция не вернёт значение, отличное от EWOULDBLOCK. Как только это произойдёт, мы можем запустить на выполнение следующую операцию с этим сокетом и вернуться к нашему опрашивающему циклу. Такая тактика (называемая в англоязычной литературе polling) работоспособна, но очень неэффективна, поскольку процессорное время тратится впустую на многократные (и безрезультатные) опросы.

Чтобы исправить ситуацию, используют функцию select. Эта функция позволяет отслеживать состояние нескольких файловых дескрипторов (а в Unix к ним относятся и сокеты) одновременно.

#include <sys/time.h>

#include <sys/types.h>

#include <unistd.h>

int select(int n, fd_set *readfds, fd_set *writefds,

               fd_set *exceptfds, struct timeval *timeout);

FD_CLR(int fd, fd_set *set);

FD_ISSET(int fd, fd_set *set);

FD_SET(int fd, fd_set *set);

FD_ZERO(int fd);

Функция select работает с тремя множествами дескрипторов, каждое из которых имеет тип fd_set. В множество readfds записываются дескрипторы сокетов, из которых нам требуется читать данные (слушающие сокеты добавляются в это же множество). Множество writefds должно содержать дескрипторы сокетов, в которые мы собираемся писать, а exceptfds - дескрипторы сокетов, которые нужно контролировать на возникновение ошибки. Если какое-то множество вас не интересуют, вы можете передать вместо указателя на него NULL. Что касается других параметров, в n нужно записать максимальное значение дескриптора по всем множествам плюс единица, а в timeout - величину таймаута. Структура timeval имеет следующий формат.

struct timeval {

   int tv_sec;     // секунды

   int tv_usec;    // микросекунды

};

Поле "микросекунды" смотрится впечатляюще. Но на практике вам не добиться такой точности измерения времени при использовании select. Реальная точность окажется в районе 100 миллисекунд.

Для работы с типом данных «множество дескрипторов» предусмотрены функции FD_XXX, показанные выше; их использование полностью скрывает от нас детали внутреннего устройства fd_set. Рассмотрим их назначение.

  •  FD_ZERO(fd_set *set) - очищает множество set 
  •  FD_SET(int fd, fd_set *set) - добавляет дескриптор fd в множество set 
  •  FD_CLR(int fd, fd_set *set) - удаляет дескриптор fd из множества set 
  •  FD_ISSET(int fd, fd_set *set) - проверяет, содержится ли дескриптор fd в множестве set 

Если хотя бы один сокет готов к выполнению заданной операции, select возвращает ненулевое значение, а все дескрипторы, которые привели к «срабатыванию» функции, записываются в соответствующие множества. Это позволяет проанализировать содержащиеся в множествах дескрипторы и выполнить над ними необходимые действия. Например, вызвать соответствующий обработчик события. Если сработал таймаут, select возвращает ноль, а в случае ошибки -1. Расширенный код записывается в errno.

Программы, использующие мультиплексирование ввода/вывода с помощью select, могут выглядеть весьма запутанными. Если в случае с fork мы строим логику программы, как будто клиент всего один, то здесь программа вынуждена отслеживать дескрипторы всех клиентов и работать с ними попеременно. Как правило, для каждого подключенного клиента хранится состояние диалога с ним.

Работа по стандартным протоколам

Сокеты могут использоваться при написании приложений, работающих по протоколам прикладного уровня Internet (HTTP, FTP, SMTP и т. д.). При этом взаимодействие клиента и сервера происходит по той же самой схеме, что и взаимодействие эхо-клиента и эхо-сервера в нашем примере. Разница в том, что данные, которыми обмениваются клиент и сервер, интерпретируются в соответствии с предписаниями соответствующего протокола.

Например, веб-сервер может работать по следующему алгоритму.

  1. Создаём слушающий сокет и привязываем его к 80-му порту (стандартный порт для HTTP-сервера).
  2. Принимаем очередной запрос на соединение.
  3. Читаем HTTP-запрос от клиента (он имеет стандартный формат и описан в RFC2616).
  4. Обрабатываем запрос и отправляем клиенту ответ, который также имеет стандартный формат.
  5. Разрываем соединение.

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

  1. Соединяемся с сервером по заданному адресу.
  2. Отправляем ему HTTP-запрос.
  3. Получаем и обрабатываем ответ сервера (например, форматируем и выводим на экран полученную HTML-страницу).
  4. Разрываем соединение.


Практические занятия по использованию Socket API

Практическое занятие. Простой TCP эхо-клиент и эхо-сервер

Задача:

Реализовать клиент-серверное приложение, использующее протокол TCP для посылки/приема эхо сообщений.

Решение:

Эхо-клиент посылает сообщение "Hello there!" и выводит на экран ответ сервера. Его код приведён в листинге 1. Эхо-сервер читает всё, что передаёт ему клиент, а затем просто отправляет полученные данные обратно. Его код содержится в листинге 2.

Листинг 1. Эхо-клиент.

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

char message[] = "Hello there!\n";

char buf[sizeof(message)];

int main()

{

   int sock;

   struct sockaddr_in addr;

   sock = socket(AF_INET, SOCK_STREAM, 0);

   if(sock < 0)

   {

       perror("socket");

       exit(1);

   }

   addr.sin_family = AF_INET;

   addr.sin_port = htons(3425); // или любой другой порт...

   addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

   if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)

   {

       perror("connect");

       exit(2);

   }

   send(sock, message, sizeof(message), 0);

   recv(sock, buf, sizeof(message), 0);

   

   printf(buf);

   close(sock);

   return 0;

}

Листинг 2. Эхо-сервер.

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

int main()

{

   int sock, listener;

   struct sockaddr_in addr;

   char buf[1024];

   int bytes_read;

   listener = socket(AF_INET, SOCK_STREAM, 0);

   if(listener < 0)

   {

       perror("socket");

       exit(1);

   }

   

   addr.sin_family = AF_INET;

   addr.sin_port = htons(3425);

   addr.sin_addr.s_addr = htonl(INADDR_ANY);

   if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)

   {

       perror("bind");

       exit(2);

   }

   listen(listener, 1);

   

   while(1)

   {

       sock = accept(listener, NULL, NULL);

       if(sock < 0)

       {

           perror("accept");

           exit(3);

       }

       while(1)

       {

           bytes_read = recv(sock, buf, 1024, 0);

           if(bytes_read <= 0) break;

           send(sock, buf, bytes_read, 0);

       }

   

       close(sock);

   }

   

   return 0;

}


Практическое занятие. Датаграммный эхо-клиент и сервер, протокол UDP

Задача:

Реализовать клиент-серверное приложение, использующее протокол UDP для посылки/приема эхо сообщений.

Решение:

Программа sender (листинг 3) отправляет сообщения "Hello there!" и "Bye bye!". Программа receiver (листинг 4) получает их и печатает на экране. Программа sender демонстрирует применение как обычного, так и присоединённого сокета, а receiver использует обычный.

Листинг 3. Программа sender.

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

char msg1[] = "Hello there!\n";

char msg2[] = "Bye bye!\n";

int main()

{

   int sock;

   struct sockaddr_in addr;

   sock = socket(AF_INET, SOCK_DGRAM, 0);

   if(sock < 0)

   {

       perror("socket");

       exit(1);

   }

   addr.sin_family = AF_INET;

   addr.sin_port = htons(3425);

   addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

   sendto(sock, msg1, sizeof(msg1), 0,

          (struct sockaddr *)&addr, sizeof(addr));

   connect(sock, (struct sockaddr *)&addr, sizeof(addr));

   send(sock, msg2, sizeof(msg2), 0);

   close(sock);

   return 0;

}

Листинг 4. Программа receiver.

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdio.h>

int main()

{

   int sock;

   struct sockaddr_in addr;

   char buf[1024];

   int bytes_read;

   sock = socket(AF_INET, SOCK_DGRAM, 0);

   if(sock < 0)

   {

       perror("socket");

       exit(1);

   }

   

   addr.sin_family = AF_INET;

   addr.sin_port = htons(3425);

   addr.sin_addr.s_addr = htonl(INADDR_ANY);

   if(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)

   {

       perror("bind");

       exit(2);

   }

   while(1)

   {

       bytes_read = recvfrom(sock, buf, 1024, 0, NULL, NULL);

       buf[bytes_read] = '\0';

       printf(buf);

   }

   

   return 0;

}


Практическое занятие. Использование параллельных процессов для параллельной обработки запросов

Задача:

Реализовать параллельную обработку на стороне сервера диалогов с подключенными клиентами с помощью множества параллельных процессов.

Решение:

Чтобы проиллюстрировать эту методику можно реализовать эхо-сервер с использованием функции fork. Результат приведен в листинге 5. Эта программа написана на языке С. Она создает новый процесс для каждого нового подключения клиента.

Листинг 5. Эхо-сервер, fork

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

int main()

{

   int sock, listener;

   struct sockaddr_in addr;

   char buf[1024];

   int bytes_read;

   listener = socket(AF_INET, SOCK_STREAM, 0);

   if(listener < 0)

   {

       perror("socket");

       exit(1);

   }

   

   addr.sin_family = AF_INET;

   addr.sin_port = htons(3425);

   addr.sin_addr.s_addr = INADDR_ANY;

   if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)

   {

       perror("bind");

       exit(2);

   }

   listen(listener, 1);

   

   while(1)

   {

       sock = accept(listener, NULL, NULL);

       if(sock < 0)

       {

           perror("accept");

           exit(3);

       }

       

       switch(fork())

       {

       case -1:

           perror("fork");

           break;

           

       case 0:

           close(listener);

           while(1)

           {

               bytes_read = recv(sock, buf, 1024, 0);

               if(bytes_read <= 0) break;

               send(sock, buf, bytes_read, 0);

           }

           close(sock);

           _exit(0);

           

       default:

           close(sock);

       }

   }

   

   close(listener);

   return 0;

}


Практическое занятие. Использование мультиплексирования ввода/вывода для параллельной обработки запросов

Задача:

Реализовать параллельную обработку на стороне сервера диалогов с подключенными клиентами с помощью мультиплексирования ввода/вывода.

Решение:

Чтобы проиллюстрировать эту методику можно реализовать эхо-сервер с использованием неблокирующих сокетов и функции select. Результат приведен в листинге 6. Эта программа написана на языке С++. Она использует контейнерный шаблонный класс std::set из библиотеки STL языка C++, чтобы облегчить работу с набором дескрипторов и сделать её более понятной.

Листинг 6. Эхо-сервер, неблокирующие сокеты и select.

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/time.h>

#include <netinet/in.h>

#include <stdio.h>

#include <unistd.h>

#include <fcntl.h>

#include <algorithm>

#include <set>

using namespace std;

int main()

{

   int listener;

   struct sockaddr_in addr;

   char buf[1024];

   int bytes_read;

   listener = socket(AF_INET, SOCK_STREAM, 0);

   if(listener < 0)

   {

       perror("socket");

       exit(1);

   }

   

   fcntl(listener, F_SETFL, O_NONBLOCK);

   

   addr.sin_family = AF_INET;

   addr.sin_port = htons(3425);

   addr.sin_addr.s_addr = INADDR_ANY;

   if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)

   {

       perror("bind");

       exit(2);

   }

   listen(listener, 2);

   

   set<int> clients;

   clients.clear();

   while(1)

   {

       // Заполняем множество сокетов

       fd_set readset;

       FD_ZERO(&readset);

       FD_SET(listener, &readset);

       for(set<int>::iterator it = clients.begin(); it != clients.end(); it++)

           FD_SET(*it, &readset);

       // Задаём таймаут

       timeval timeout;

       timeout.tv_sec = 15;

       timeout.tv_usec = 0;

       // Ждём события в одном из сокетов

       int mx = max(listener, *max_element(clients.begin(), clients.end()));

       if(select(mx+1, &readset, NULL, NULL, &timeout) <= 0)

       {

           perror("select");

           exit(3);

       }

       

       // Определяем тип события и выполняем соответствующие действия

       if(FD_ISSET(listener, &readset))

       {

           // Поступил новый запрос на соединение, используем accept

           int sock = accept(listener, NULL, NULL);

           if(sock < 0)

           {

               perror("accept");

               exit(3);

           }

           

           fcntl(sock, F_SETFL, O_NONBLOCK);

           clients.insert(sock);

       }

       for(set<int>::iterator it = clients.begin(); it != clients.end(); it++)

       {

           if(FD_ISSET(*it, &readset))

           {

               // Поступили данные от клиента, читаем их

               bytes_read = recv(*it, buf, 1024, 0);

               if(bytes_read <= 0)

               {

                   // Соединение разорвано, удаляем сокет из множества

                   close(*it);

                   clients.erase(*it);

                   continue;

               }

               // Отправляем данные обратно клиенту

               send(*it, buf, bytes_read, 0);

           }

       }

   }

   

   return 0;

}


Использованная литература

  1.  Алексей Федосеев. UNIX: учебный курс. Linux User Group МГТУ им. Н.Э. Баумана. 2006 - http://www.openspin.org/materials/courses/admin/
  2.  Александр Шаргин. Программирование сокетов в Linux. 2006 - http://www.rsdn.ru/article/unix/sockets.xml
  3.  Олифер В.Г., Олифер Н.А. Компьютерные сети. Принципы, технологии, протоколы. — СПб.: Питер, 2002. — 672 с.: ил.
  4.  Курячий Г.В., Маслинский К.А. Операционная система Linux. — М.: Интуит.Ру, 2005. — 392 с.: ил.
  5.  Рейчардс К., Фостер-Джонсон Э. UNIX: справочник. – СПб.: Питер Ком, 1999. – 384 с.: ил.

43


 

А также другие работы, которые могут Вас заинтересовать

81198. Синтоизм как национальная религия Японии 24.16 KB
  Императорский двор стал руководить деятельностью главных синтоистских храмов; наиболее важные обряды стал совершать император объявленный в 7 в. буддизм играл преобладающую роль в религиозной жизни страны многие японцы стали исповедовать две религии. постепенно стал восстанавливать своё влияние и несколько модернизировал своё учение.
81199. Вероучение и культ синтоизма 22.07 KB
  Священными книгами синтоизма считаются Кодзики и Нихон секи. Кодзики содержит мифы космогонического и героического характера. Нихон секи представляет собой в основном японские исторические хроники есть там и мифы о происхождении мира и Японии частично повторяющие сюжеты Кодзики.
81200. Специфика зороастризма 22.95 KB
  Выделяют два самых важных божества: Ахура – Мазда светлое божество олицетворявшее мудрость правду и Ангра – Манью тёмный бог. прежде всего почитался АхураМазда осознававшийся как Богтворец воплощение и носитель блага. Демонов противостоящих ахурам возглавляет главный носитель зла АнхраМанью. Борьба Ахура Мазды и Анхра Манью изначальна бескомпромиссна и каждый человек должен занять свою личную позицию в ней.
81201. История формирования иудаизма 26.17 KB
  Выделяют различные периоды формирования и развития иудаизма. Возникновение иудаизма как религии принято связывать с именем Моисея получивший на горе Синай через Откровение десять заповедей образовавших основу монотеизма и религиозной этики. формируются основные черты иудаизма: строгий монотеизм централизация культа канонизация священных книг появлению веры в сверхъестественную помощь для освобождения от угнетателей и веры в избавителямессию.
81202. Догматы и культ иудаизма 26.33 KB
  Центральная доктрина иудаизма вера в единого Бога который бессмертен вечен всемогущ вездесущ и безграничен. В соответствии с нормами иудаизма верующий поддерживает связь с Богом через молитву а божья воля открывается человеку через Танах.
81203. Структура Ветхого завета в иудаизме 22.12 KB
  Книги Ветхого Завета были написаны в период с XIII по I в. Ветхий Завет состоит из следующих книг: 1 Книги закона Тора Учение или Пятикнижие Моисеево составление книг приписывается Моисею: Бытие сотворение мира и человека рай первые люди грехопадение размножение человечества всемирный потоп Ной патриархи родоначальники еврейского народа Авраам Исаак Иаков Иосиф с братьями поселение евреев в Египте; Исход Моисей 10 заповедей освобождение из плена; Левит религиозное законодательство; Числа законодательство и...
81204. Формирование ислама. Жизнь и деятельность Мухаммеда 25.03 KB
  Жизнь и деятельность Мухаммеда. Политическое и религиозное движение возглавил пророк Мухаммед. Мухаммед родился в 570 г. Мать Амина по обычаю мекканцев отдала Мухаммеда кормилицебедуинке у которой он рос до 5 лет.
81205. Вероучение ислама 24.39 KB
  Иман или вера включает: Веру в Единого Бога Аллаха. Веру в Ангелов и демонов. 3Веру в Святые Писания и в святость Корана который считается словом божьим божественным откровением которое передавал Аллах в виде видений Мухаммеду в течение 22 лет т. 4Веру в Пророков и в посланничество Мухаммеда.