Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
14-24
Следующие два вызова, fgets
и sendto
, выполняются так же, как и в предыдущих версиях этой функции. Но поскольку мы посылаем широковещательную дейтаграмму, мы можем получить множество ответов. Мы вызываем в цикле функцию recvfrom
и выводим все ответы, полученные в течение 5 с. По истечении 5 с генерируется сигнал SIGALRM
, вызывается наш обработчик сигнала и функция recvfrom
возвращает ошибку EINTR
.
25-29
Для каждого полученного ответа мы вызываем функцию sock_ntop_host
, которая в случае IPv4 возвращает строку, содержащую IP-адрес сервера в точечно-десятичной записи. Эта строка выводится вместе с ответом сервера.
Если мы запустим программу, задав широковещательный адрес подсети 192. 168.42.255, мы увидим следующее:
bsdi % udpcli01 192.168.42.255 hi
from 192 168.42 2: Sat Aug 2 16.42.45 2003
from 192.168.42.1: Sat Aug 2 14.42.45 2003
from 192.168.42.3: Sat Aug 2 14.42.45 2003
hello
from 192.168.42.3: Sat Aug 2 14.42.57 2003
from 192.168.42.2: Sat Aug 2 16.42.57 2003
from 192.168.42.1: Sat Aug 2 14.42.57 2003
Каждый раз мы набираем строку ввода, чтобы сгенерировать выходную дейтаграмму UDP, и каждый раз получаем три ответа, причем отвечает и отправляющий узел. Как мы отмечали ранее, получателями широковещательной дейтаграммы являются все узлы в сети, включая отправляющий. Каждый ответ является направленным, поскольку адрес отправителя запроса, используемый каждым сервером в качестве адреса получателя ответа, — это адрес направленной передачи.
Все системы сообщают одно и то же время, поскольку на них используется NTP (Network Time Protocol — протокол синхронизации времени).
Фрагментация IP-пакетов и широковещательная передача
В Беркли-ядрах фрагментация широковещательных дейтаграмм запрещена. Если размер IP-дейтаграммы, посылаемой на широковещательный адрес, превышает размер MTU исходящего интерфейса, возвращается ошибка EMSGSIZE
[128, с. 233–234]. Эта стратегия впервые появилась в 4.2BSD. На самом деле нет никаких технических препятствий для фрагментирования широковещательных дейтаграмм, но широковещательная передача сама по себе связана со значительной нагрузкой на сеть, поэтому не стоит дополнительно увеличивать эту нагрузку, используя фрагментацию.
Можно наблюдать этот сценарий с нашей программой из листинга 20.1. Мы перенаправляем стандартный поток ввода для чтения из файла, содержащего 2000-байтовую строку, которая потребует фрагментации в Ethernet:
bsdi % udpcli01 192.168.42.255
sendto error: Message too long
Это ограничение реализовано в AIX, FreeBSD и MacOS. Linux, Solaris и HP-UX фрагментируют дейтаграммы, отправленные на широковещательный адрес. Однако в целях переносимости приложение, которому нужно сделать широковещательный запрос, должно определять MTU для интерфейса, через который будет отправлено сообщение, при помощи параметра SIOCGIPMTU функции ioctl, после чего вычесть размер заголовков IP и транспортного протокола. Альтернативный подход: выбрать типичное значение MTU (например, 1500 для Ethernet) и использовать его в качестве константы.
20.5. Ситуация гонок
Ситуация гонок ( race condition ) обычно возникает, когда множество процессов получают доступ к общим для них данным, но корректность результата зависит от порядка выполнения процессов. Поскольку порядок выполнения процессов в типичных системах Unix зависит от множества факторов, которые могут меняться от запуска к запуску, иногда результат корректен, а иногда — нет. Наиболее сложным для отладки типом гонок является такой, когда результат получается некорректным только изредка. Более подробно о ситуациях гонок мы поговорим в главе 26, когда будем обсуждать взаимные исключения (mutex) и условные переменные (condition variables). При программировании потоков всегда возникают проблемы с ситуациями гонок, поскольку значительное количество данных является общим для всех потоков (например, все глобальные переменные).
Ситуации гонок другого типа часто возникают при работе с сигналами. Проблемы возникают, потому что сигнал, как правило, может быть доставлен в любой момент во время выполнения нашей программы. POSIX позволяет нам блокировать доставку сигнала, но при выполнении операций ввода-вывода это часто не дает эффекта.
Чтобы понять эту проблему, рассмотрим пример. Ситуация гонок возникает при выполнении программы из листинга 20.1. Потратьте несколько минут и посмотрите, сможете ли вы ее обнаружить. ( Подсказка : в каком месте программы мы можем находиться, когда доставляется сигнал?) Вы можете также инициировать ситуацию гонок следующим образом: изменить аргумент функции alarm
с 5 на 1 и добавить вызов sleep(1)
сразу же после printf
.
Когда мы после внесения этих изменений наберем первую строку ввода, эта строка будет отправлена как широковещательное сообщение, а мы установим аргумент функции alarm
равным 1 с. Мы блокируемся в вызове функции recvfrom
, а затем для нашего сокета приходит первый ответ, вероятно, в течение нескольких миллисекунд. Ответ возвращается функцией recvfrom
, но затем мы входим в спящее состояние на одну секунду. Принимаются остальные ответы и помещаются в приемный буфер сокета. Но пока мы находимся в спящем состоянии, время таймера alarm
истекает и генерируется сигнал SIGALRM
. При этом вызывается наш обработчик сигнала, затем он возвращает управление и прерывает функцию sleep
, в которой мы блокированы. Далее мы повторяем цикл и читаем установленные в очередь ответы с паузой в одну секунду каждый раз, когда выводится ответ. Прочитав все ответы, мы снова блокируемся в вызове функции recvfrom
, однако таймер уже не работает. Мы окажемся навсегда заблокированы в вызове функции recvfrom
. Фундаментальная проблема здесь в том, что наша цель — обеспечить прерывание блокирования в функции recvfrom
обработчиком сигнала, однако сигнал может быть доставлен в любое время, и наша программа в момент доставки сигнала может находиться в любом месте бесконечного цикла for
.
Теперь мы проанализируем четыре различных варианта решения этой проблемы: одно некорректное и три различных корректных решения.
Наше первое (некорректное) решение снижает вероятность появления ошибки, блокируя сигнал и предотвращая его доставку, пока наша программа выполняет оставшуюся часть цикла for
. Эта версия представлена в листинге 20.2.
Листинг 20.2. Блокирование сигналов при выполнении в цикле for (некорректное решение)
//bcast/dgclibcast3.c
Интервал:
Закладка: