Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
8.14. Определение исходящего интерфейса для UDP
С помощью присоединенного сокета UDP можно также задавать исходящий интерфейс, который будет использован для отправки дейтаграмм к определенному получателю. Это объясняется побочным эффектом функции connect
, примененной к сокету UDP: ядро выбирает локальный IP-адрес (предполагается, что процесс еще не вызвал функцию bind
для явного его задания). Локальный адрес выбирается в процессе поиска адреса получателя в таблице маршрутизации, причем берется основной IP-адрес интерфейса, с которого, согласно таблице, будут отправляться дейтаграммы.
В листинге 8.13 показана простая программа UDP, которая с помощью функции connect соединяется с заданным IP-адресом и затем вызывает функцию getsockname
, выводя локальный IP-адрес и порт.
Листинг 8.13. Программа UDP, использующая функцию connect для определения исходящего интерфейса
//udpcliserv/udpcli09.c
1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4 {
5 int sockfd;
6 socklen_t len;
7 struct sockaddr_in cliaddr, servaddr;
8 if (argc != 2)
9 err_quit("usage: udpcli ");
10 sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
11 bzero(&servaddr, sizeof(servaddr));
12 servaddr.sin_family = AF_INET;
13 servaddr.sin_port = htons(SERV_PORT);
14 Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
15 Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));
16 len = sizeof(cliaddr);
17 Getsockname(sockfd, (SA*)&cliaddr, &len);
18 printf("local address %s\n", Sock_ntop((SA*)&cliaddr, len));
19 exit(0);
20 }
Если мы запустим программу на узле freebsd
с несколькими сетевыми интерфейсами, то получим следующий вывод:
freebsd % udpcli09 206.168.112.96
local address 12.106.32.254:52329
freebsd % udpcli09 192.168.42.2
local address 192.168.42.1:52330
freebsd % udpcli09 127.0.0.1
local address 127.0.0.1:52331
По рис. 1.7 видно, что когда мы запускаем программу первые два раза, аргументом командной строки является IP-адрес в разных сетях Ethernet. Ядро присваивает локальный IP-адрес первичному адресу интерфейса в соответствующей сети Ethernet. При вызове функции connect
на сокете UDP ничего не отправляется на этот узел — это полностью локальная операция, которая сохраняет IP-адрес и порт собеседника. Мы также видим, что вызов функции connect на неприсоединенном сокете UDP также присваивает сокету динамически назначаемый порт.
К сожалению, эта технология действует не во всех реализациях, что особенно касается ядер, происходящих от SVR4. Например, это не работает в Solaris 2.5, но работает в AIX, Digital Unix, Linux, MacOS X и Solaris 2.6.
8.15. Эхо-сервер TCP и UDP, использующий функцию select
Теперь мы объединим наш параллельный эхо-сервер TCP из главы 5 и наш последовательный эхо-сервер UDP из данной главы в один сервер, использующий функцию select
для мультиплексирования сокетов TCP и UDP. В листинге 8.14 представлена первая часть этого сервера.
Листинг 8.14. Первая часть эхо-сервера, обрабатывающего сокеты TCP и UDP при помощи функции select
//udpcliserv/udpservselect01.c
1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4 {
5 int listenfd, connfd, udpfd, nready, maxfdp1;
6 char mesg[MAXLINE];
7 pid_t childpid;
8 fd_set rset;
9 ssize_t n;
10 socklen_t len;
11 const int on = 1;
12 struct sockaddr_in cliaddr, servaddr;
13 void sig_chld(int);
14 /* создание прослушиваемого сокета TCP */
15 listenfd = Socket(AF_INET, SOCK_STREAM, 0);
16 bzero(&servaddr, sizeof(servaddr));
17 servaddr.sin_family = AF_INET;
18 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
19 servaddr.sin_port = htons(SERV_PORT);
20 Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
21 Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
22 Listen(listenfd, LISTENQ);
23 /* создание сокета UDP */
24 udpfd = Socket(AF_INET, SOCK_DGRAM, 0);
25 bzero(&servaddr, sizeof(servaddr));
26 servaddr.sin_family = AF_INET;
27 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
28 servaddr.sin_port = htons(SERV_PORT);
29 Bind(udpfd, (SA*)&servaddr, sizeof(servaddr));
14-22
Создается прослушиваемый сокет TCP, который связывается с заранее известным портом сервера. Мы устанавливаем параметр сокета SO_REUSEADDR
в случае, если на этом порте существуют соединения.
23-29
Также создается сокет UDP и связывается с тем же портом. Даже если один и тот же порт используется для сокетов TCP и UDP, нет необходимости устанавливать параметр сокета SO_REUSEADDR
перед этим вызовом функции bind
, поскольку порты TCP не зависят от портов UDP.
В листинге 8.15 показана вторая часть нашего сервера.
Листинг 8.15. Вторая половина эхо-сервера, обрабатывающего TCP и UDP при помощи функции select
udpcliserv/udpservselect01.c
30 Signal(SIGCHLD, sig_chld); /* требуется вызвать waitpid() */
31 FD_ZERO(&rset);
32 maxfdp1 = max(listenfd, udpfd) + 1;
33 for (;;) {
34 FD_SET(listenfd, &rset);
35 FD_SET(udpfd, &rset);
36 if ((nready = select(maxfdp1, &rset, NULL, NULL, NULL)) < 0) {
37 if (errno == EINTR)
38 continue; /* назад в for() */
39 else
40 err_sys("select error");
41 }
42 if (FD_ISSET(listenfd, &rset)) {
43 len = sizeof(cliaddr);
44 connfd = Accept(listenfd, (SA*)&cliaddr, &len);
45 if ((childpid = Fork()) == 0) { /* дочерний процесс */
46 Close(listenfd); /* закрывается прослушиваемый сокет */
47 str_echo(connfd); /* обработка запроса */
48 exit(0);
49 }
50 Close(connfd); /* родитель закрывает присоединенный сокет */
51 }
52 if (FD_ISSET(udpfd, &rset)) {
53 len = sizeof(cliaddr);
54 n = Recvfrom(udpfd, mesg, MAXLINE, 0, (SA*)&cliaddr, &len);
55 Sendto(udpfd, mesg, n, 0, (SA*)&cliaddr, len);
56 }
57 }
58 }
30
Для сигнала SIGCHLD
устанавливается обработчик, поскольку соединения TCP будут обрабатываться дочерним процессом. Этот обработчик сигнала мы показали в листинге 5.8.
31-32
Мы инициализируем набор дескрипторов для функции select
и вычисляем максимальный из двух дескрипторов, готовности которого будем ожидать.
34-41
Мы вызываем функцию select
, ожидая только готовности к чтению прослушиваемого сокета TCP или сокета UDP. Поскольку наш обработчик сигнала sig_chld
может прервать вызов функции select
, обрабатываем ошибку EINTR
.
42-51
С помощью функции accept
мы принимаем новое клиентское соединение, а когда прослушиваемый сокет TCP готов для чтения, с помощью функции fork
порождаем дочерний процесс и вызываем нашу функцию str_echo
в дочернем процессе. Это та же последовательность действий, которую мы выполняли в главе 5.
52-57
Если сокет UDP готов для чтения, дейтаграмма пришла. Мы читаем ее с помощью функции recvfrom
и отправляем обратно клиенту с помощью функции sendto
.
Интервал:
Закладка: