Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
87-89
Если мы достигли максимально возможного количества повторных передач, возвращается значение -1, указывающее вызывающему процессу, что дальнейшие попытки передачи должны прекратиться. В противном случае возвращается 0.
В нашем примере клиент соединялся дважды с двумя различными эхо-серверами в Интернете утром рабочего дня. Каждому серверу было отправлено по 500 строк. По пути к первому серверу было потеряно 8 пакетов, по пути ко второму — 16. Один из потерянных шестнадцати пакетов, предназначенных второму серверу, был потерян дважды, то есть пакет пришлось дважды передавать повторно, прежде чем был получен ответ. Все остальные потерянные пакеты пришлось передать повторно только один раз. Мы могли убедиться, что эти пакеты были действительно потеряны, посмотрев на выведенные порядковые номера каждого из полученных пакетов. Если пакет лишь опоздал, но не был потерян, после повторной передачи клиент получает два ответа: соответствующий запоздавшему первому пакету и повторно переданному. Обратите внимание, что у нас нет возможности определить, что именно было потеряно (и привело к необходимости повторной передачи клиентского запроса) — сам клиентский запрос или же ответ сервера, высланный после получения такого запроса.
Для первого издания этой книги автор написал для проверки этого клиента сервер UDP, который случайным образом игнорировал пакеты. Теперь он не используется. Нужно только соединить клиент с сервером через Интернет, и тогда нам почти гарантирована потеря некоторых пакетов!
22.6. Связывание с адресами интерфейсов
Одно из типичных применений функции get_ifi_info
связано с приложениями UDP, которым нужно выполнять мониторинг всех интерфейсов на узле, чтобы знать, когда и на какой интерфейс приходит дейтаграмма. Это позволяет получающей программе узнавать адрес получателя дейтаграммы UDP, так как именно по этому адресу определяется сокет, на который доставляется дейтаграмма, даже если узел не поддерживает параметр сокета IP_RECVDSTADDR
.
Вспомните наше обсуждение в конце раздела 22.2. Если узел использует более распространенную модель системы с гибкой привязкой (см. раздел 8.8), IP-адрес получателя может отличаться от IP-адреса принимающего интерфейса. В этом случае мы можем определить только адрес получателя дейтаграммы, который не обязательно должен быть адресом, присвоенным принимающему интерфейсу. Чтобы определить принимающий интерфейс, требуется параметр сокета IP_RECVIF или IPV6_PKTINFO.
В листинге 22.13 показана первая часть примера применения этой технологии к эхо-серверу UDP, который связывается со всеми адресами направленной передачи, широковещательной передачи и, наконец, с универсальными адресами.
Листинг 22.13. Первая часть сервера UDP, который с помощью функции bind связывается со всеми адресами
//advio/udpserv03.c
1 #include "unpifi.h"
2 void mydg_echo(int, SA*, socklen_t, SA*);
3 int
4 main(int argc, char **argv)
5 {
6 int sockfd;
7 const int on = 1;
8 pid_t pid;
9 struct ifi_info *ifi, *ifihead;
10 struct sockaddr_in *sa, cliaddr, wildaddr;
11 for (ifihead = ifi = Get_ifi_info(AF_INET, 1);
12 ifi != NULL; ifi = ifi->ifi_next) {
13 /* связываем направленный адрес */
14 sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
15 Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
16 sa = (struct sockaddr_in*)ifi->ifi_addr;
17 sa->sin_family = AF_INET;
18 sa->sin_port = htons(SERV_PORT);
19 Bind(sockfd, (SA*)sa, sizeof(*sa));
20 printf("bound %s\n", Sock_ntop((SA*)sa, sizeof(*sa)));
21 if ((pid = Fork()) == 0) { /* дочерний процесс */
22 mydg_echo(sockfd, (SA*)&cliaddr, sizeof(cliaddr), (SA*)sa);
23 exit(0); /* не выполняется */
24 }
11-12
Функция get_ifi_info
получает все адреса IPv4, включая дополнительные (псевдонимы), для всех интерфейсов. Затем программа перебирает все структуры ifi_info
.
13-20
Создается сокет UDP, и с ним связывается адрес направленной передачи. Мы также устанавливаем параметр сокета SO_REUSEADDR
, поскольку мы связываем один и тот же порт (параметр SERV_PORT
) для всех IP-адресов.
Не все реализации требуют, чтобы был установлен этот параметр сокета. Например, Беркли-реализации не требуют этого параметра и позволяют с помощью функции bind связать уже связанный порт, если новый связываемый IP-адрес не является универсальным адресом и отличается от всех IP-адресов, уже связанных с портом. Однако Solaris 2.5 для успешного связывания с одним и тем же портом второго адреса направленной передачи требует установки этого параметра.
21-24
Вызывается функция fork
, порождающая дочерний процесс. В этом дочернем процессе вызывается функция mydg_echo
, которая ждет прибытия любой дейтаграммы на сокет и отсылает ее обратно отправителю.
В листинге 22.14 показана следующая часть функции main
, которая обрабатывает широковещательные адреса.
Листинг 22.14. Вторая часть сервера UDP, который с помощью функции bind связывается со всеми адресами
//advio/udpserv03.c
25 if (ifi->ifi_flags & IFF_BROADCAST) {
26 /* пытаемся связать широковещательный адрес */
27 sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
28 Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
29 sa = (struct sockaddr_in*)ifi->ifi_brdaddr;
30 sa->sin_family = AF_INET;
31 sa->sin_port = htons(SERV_PORT);
32 if (bind(sockfd, (SA*)sa, sizeof(*sa)) < 0) {
33 if (errno == EADDRINUSE) {
34 printf("EADDRINUSE: %s\n",
35 Sock_ntop((SA*)sa, sizeof(*sa)));
36 Close(sockfd);
37 continue;
38 } else
39 err_sys("bind error for %s",
40 Sock_ntop((SA*)sa, sizeof(*sa)));
41 }
42 printf("bound %s\n", Sock_ntop((SA*)sa, sizeof(*sa)));
43 if ((pid = Fork()) == 0) { /* дочерний процесс */
44 mydg_echo(sockfd, (SA*)&cliaddr, sizeof(cliaddr),
45 (SA*)sa);
46 exit(0); /* не выполняется */
47 }
48 }
49 }
25-42
Если интерфейс поддерживает широковещательную передачу, создается сокет UDP и с ним связывается широковещательный адрес. На этот раз мы позволим функции bind
завершиться с ошибкой EADDRINUSE
, поскольку если у интерфейса имеется несколько дополнительных адресов (псевдонимов) в одной подсети, то каждый из различных адресов направленной передачи будет иметь один и тот же широковещательный адрес. Подобный пример приведен после листинга 17.3. В этом сценарии мы предполагаем, что успешно выполнится только первая функция bind
.
43-47
Порождается дочерний процесс, и он вызывает функцию mydg_echo
.
Интервал:
Закладка: