UNIX: разработка сетевых приложений
12 if (cred.cmcred_ngroups == 0) {13 printf("(no credentials returned)\n");14 } else {15 printf("PID of sender = %d\n", cred.cmcred_pid);16 printf("real user ID = %d\n", cred.cmcred_uid);17 printf("real group ID = %d\n", cred.cmcred_gid);18 printf("effective user ID = %d\n", cred.cmcred_euid);19 printf("%d groups:", cred.cmcred_ngroups - 1);20 for (i = 1; i < cred.cmcred_ngroups; i++)21 printf(" %d", cred.cmcred_groups[i]);22 printf("\n");23 }24 Writen(sockfd, buf, n);25 }26 if (n < 0 && errno == EINTR)27 goto again;28 else if (n < 0)29 err_sys("str_echo: read error");30 }Если идентифицирующие данные возвращаются, они выводятся.11-23Оставшаяся часть цикла не меняется. Этот код считывает строки от клиента и затем отправляет их обратно клиенту.24-25Наш клиент, представленный в листинге 15.4, остается практически неизменным. Мы добавляем передачу пустой структуры
при вызовеcmsgcred, которая заполняется ядром.sendmsgПеред запуском клиента определим свои личные данные командой
:idfreebsd % <b>id</b>uid=1007(andy) gid=1007(andy) groups=1007(andy), 0(wheel)Если мы запустим сервер в одном окне, а клиент в другом, то для сервера после однократного выполнения клиента получим представленный ниже вывод.
freebsd % <b>unixstrserv02</b>PID of sender = 26881real user ID = 1007real group ID = 1007effective user ID = 10072 groups: 1007 0Информация выводится только после отправки клиентом данных серверу. Мы видим, что сведения соответствуют тем, которые были получены командой
.id15.9. Резюме
Доменные сокеты Unix являются альтернативой IPC, когда клиент и сервер находятся на одном узле. Преимущество использования доменных сокетов Unix перед некоторой формой IPC состоит в том, что используемый API практически идентичен клиент-серверному сетевому соединению. Преимущество использования доменных сокетов Unix перед TCP, когда клиент и сервер находятся на одном узле, заключается в повышенной производительности доменных сокетов Unix относительно TCP во многих реализациях.
Мы изменили наш эхо-сервер и эхо-клиент TCP и UDP для использования доменных протоколов Unix, и единственным главным отличием оказалась необходимость при помощи функции
связывать полное имя с клиентским сокетом UDP так, чтобы серверу UDP было куда отправлять ответы.bindПередача дескрипторов между клиентами и серверами, находящимися на одном узле, — это мощная технология, которая используется при работе с доменными сокетами Unix. Мы показали пример передачи дескриптора от дочернего процесса обратно родительскому процессу в разделе 15.7. В разделе 28.7 мы покажем пример, в котором клиент и сервер не будут родственными, а в разделе 30.9 — другой пример, когда дескриптор передается от родительского процесса дочернему.
Упражнения
1. Что произойдет, если доменный сервер Unix вызовет функцию
после вызова функцииunlink?bind2. Что произойдет, если доменный сервер Unix при завершении не отсоединит с помощью функции
свое известное полное имя, а клиент будет пытаться с помощью функцииunlinkсоединиться с сервером через некоторое время после того, как тот завершит работу?connect3. Измените листинг 11.5 так, чтобы после того как будет выведен адрес протокола собеседника, вызывалась бы функция
, а также чтобы вывести число байтов, возвращаемых функциейsleep(5)всякий раз, когда она возвращает положительное значение. Измените листинг 11.8 так, чтобы для каждого байта результата, отправляемого клиенту, вызывалась функцияread. (Мы обсуждаем подобные изменения в решении упражнения 1.5.) Запустите клиент и сервер на одном узле, используя TCP. Сколько байтов считывает клиент с помощью функцииwrite?readЗапустите клиент и сервер на одном узле, используя доменный сокет Unix. Изменилось ли что-нибудь?
Теперь для сервера вместо функции write вызовите функцию
и задайте флагsend(чтобы выполнить это упражнение, вам нужно использовать Беркли-реализацию). Запустите клиент и сервер на одном узле, используя доменный сокет Unix. Изменилось ли что-нибудь?MSG_EOR4. Напишите программу, определяющую значения, показанные в табл. 4.6. Один из подходов — создать потоковый канал и затем с помощью функции
разветвить родительский и дочерний процессы. Родительский процесс входит в циклfork, увеличивая на каждом шаге значениеforот 0 до 14. Каждый раз при прохождении цикла родительский процесс сначала записывает значениеbacklogв потоковый канал. Дочерний процесс читает это значение, создает прослушиваемый сокет, связанный с адресом закольцовки, и присваиваетbacklogсчитанное значение. Затем дочерний процесс делает запись в потоковый канал просто для того, чтобы сообщить родительскому процессу о своей готовности. Затем родительский процесс пытается установить как можно больше соединений, задав предварительно аргумент функцииbacklogравным 2 с, поскольку при достижении предельного значенияalarmвызов функции connect заблокируется, и отправляет еще раз сегментbacklog. Дочерний процесс никогда не вызывает функциюSYN, что позволяет ядру установить в очередь все соединения с родительским процессом. Когда истекает время ожидания родительского процесса (аргумент функцииaccept, в данном случае 2 с), по счетчику цикла он может определить, какая по счету функцияalarmсоответствует предельному значениюconnect. Затем родительский процесс закрывает свои сокеты и пишет следующее новое значение в потоковый канал для дочернего процесса. Когда дочерний процесс считывает новое значение, он закрывает прежний прослушиваемый сокет и создает новый, заново начиная процедуру.backlog