UNIX: разработка сетевых приложений
Порождение дочернего процессаПорождается дочерний процесс, и он вызывает функцию43-47.mydg_echoЗаключительная часть функции
показана в листинге 22.15. В этом коде при помощи функцииmainпроисходит связывание с универсальным адресом для обработки любого адреса получателя, отличного от адресов направленной и широковещательной передачи, которые уже связаны. На этот сокет будут приходить только дейтаграммы, предназначенные для ограниченного широковещательного адреса (255.255.255.255).bindЛистинг 22.15. Заключительная часть сервера UDP, связывающегося со всеми адресами
//advio/udpserv03.c50 /* связываем универсальный адрес */51 sockfd = Socket(AF_INET, SOCK_DGRAM, 0);52 Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));53 bzero(&wildaddr, sizeof(wildaddr));54 wildaddr.sin_family = AF_INET;55 wildaddr.sin_addr.s_addr = htonl(INADDR_ANY);56 wildaddr.sin_port = htons(SERV_PORT);57 Bind(sockfd, (SA*)&wildaddr, sizeof(wildaddr));58 printf("bound %s\n", Sock_ntop((SA*)&wildaddr, sizeof(wildaddr)));59 if ((pid = Fork()) == 0) { /* дочерний процесс */60 mydg_echo(sockfd, (SA*)&cliaddr, sizeof(cliaddr), (SA*)sa);61 exit(0); /* не выполняется */62 }63 exit(0);64 }Создание сокета и связывание с универсальным адресомСоздается сокет UDP, устанавливается параметр сокета50-62и происходит связывание с универсальным IP-адресом. Порождается дочерний процесс, вызывающий функциюSO_REUSEADDR.mydg_echoЗавершение работы функции mainФункция63завершается, и сервер продолжает выполнять работу, как и все порожденные дочерние процессы.mainФункция
, которая выполняется всеми дочерними процессами, показана в листинге 22.16.mydg_echoЛистинг 22.16. Функция mydg_echo
//advio/udpserv03.c65 void66 mydg_echo(int sockfd, SA *pcliaddr, socklen_t clilen, SA *myaddr)67 {68 int n;69 char mesg[MAXLINE];70 socklen_t len;71 for (;;) {72 len = clilen;73 n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);74 printf("child %d, datagram from %s", getpid(),75 Sock_ntop(pcliaddr, len));76 printf(", to %s\n", Sock_ntop(myaddr, clilen));77 Sendto(sockfd, mesg, n, 0, pcliaddr, len);78 }79 }Новый аргументЧетвертым аргументом этой функции является IP-адрес, связанный с сокетом. Этот сокет должен получать только дейтаграммы, предназначенные для данного IP-адреса. Если IP-адрес является универсальным, сокет должен получать только те дейтаграммы, которые не подходят ни для какого другого сокета, связанного с тем же портом.65-66Чтение дейтаграммы и отражение ответаДейтаграмма читается с помощью функции71-78и отправляется клиенту обратно с помощью функцииrecvfrom. Эта функция также выводит IP-адрес клиента и IP-адрес, который был связан с сокетом.sendtoЗапустим эту программу на нашем узле
после установки псевдонима для интерфейсаsolarisEthernet. Адрес псевдонима: узел 200 в сети 10.0.0/24.hme0solaris % <b>udpserv03</b>bound 127.0.0.1:9877 <i>интерфейс закольцовки</i>bound 10.0.0.200:9877 <i>направленный адрес интерфейса hme0:1</i>bound 10.0.0.255:9877 <i>широковещательный адрес интерфейса hme0:1</i>bound 192.168.1.20:9877 <i>направленный адрес интерфейса hme0</i>bound 192.168.1.255:9877 <i>широковещательный адрес интерфейса hme0</i>bound 0.0.0.0.9877 <i>универсальный адрес</i>При помощи утилиты
мы можем проверить, что все сокеты связаны с указанными IP-адресами и портом:netstatsolaris % <b>netstat -na | grep 9877</b>127.0.0.1.9877 Idle10.0.0.200.9877 Idle*.9877 Idle192.129.100.100.9877 Idle*.9877 Idle*.9877 IdleСледует отметить, что для простоты мы создаем по одному дочернему процессу на сокет, хотя возможны другие варианты. Например, чтобы ограничить число процессов, программа может управлять всеми дескрипторами сама, используя функцию
и не вызывая функциюselect. Проблема в данном случае будет заключаться в усложнении кода. Хотя использовать функциюforkдля всех дескрипторов несложно, нам придется осуществить некоторое сопоставление каждого дескриптора связанному с ним IP-адресу (вероятно, с помощью массива структур), чтобы иметь возможность вывести IP-адрес получателя после того, как на определенном сокете получена дейтаграмма. Часто бывает проще использовать отдельный процесс или поток для каждой операции или дескриптора вместо мультиплексирования множества различных операций или дескрипторов одним процессом.select22.7. Параллельные серверы UDP
Большинство серверов UDP являются последовательными (iterative): сервер ждет запрос клиента, считывает запрос, обрабатывает его, отправляет обратно ответ и затем ждет следующий клиентский запрос. Но когда обработка запроса клиента занимает длительное время, желательно так или иначе совместить во времени обработку различных запросов.