Применение интерфейса сетевых
Рассмотренный нами интерфейс умеет работать не только со стеком протоколов TCP/IP, но и с другими семействами протоколов. При этом требуется лишь незначительное изменение написанных с его помощью программ. Рассмотрим действия, которые необходимо выполнить для модернизации написанных для TCP/IP программ под другое семейство протоколов.
- Изменяется тип сокета, поэтому для его точной спецификации нужно задавать другие параметры в системном вызове socket().
- В различных семействах протоколов применяются различные адресные пространства для удаленных и локальных адресов сокетов. Поэтому меняется состав структуры для хранения полного адреса сокета, название ее типа, наименования полей и способ их заполнения.
- Описание типов данных и предопределенных констант будет находиться в других include-файлах, поэтому потребуется заменить include-файлы <netinet/in.h> и <arpa/inet.h> на файлы, относящиеся к выбранному семейству протоколов.
- Может измениться способ вычисления фактической длины полного адреса сокета и указания его максимального размера.
И все!!!
Рис. 15-16.9. Схема работы TCP-сервера с параллельной обработкой запросов
Давайте подробнее рассмотрим эти изменения на примере семейства UNIX Domain протоколов. Семейство UNIX Domain протоколов предназначено для общения локальных процессов с использованием интерфейса системных вызовов. Оно содержит один потоковый и один датаграммный протокол. Никакой сетевой интерфейс при этом не используется, а вся передача информации реально происходит через адресное пространство ядра операционной системы. Многие программы, взаимодействующие и с локальными, и с удаленными процессами (например, X-Windows), для локального общения используют этот стек протоколов.
Поскольку общение происходит в рамках одной вычислительной системы, в полном адресе сокета его удаленная часть отсутствует. В качестве адресного пространства портов – локальной части адреса – выбрано адресное пространство, совпадающее с множеством всех допустимых имен файлов в файловой системе.
При этом в качестве имени сокета требуется задавать имя несуществующего еще файла в директории, к которой у вас есть права доступа как на запись, так и на чтение. При настройке адреса (системный вызов bind()) под этим именем будет создан файл типа "сокет" – последний еще неизвестный нам тип файла. Этот файл для сокетов играет роль файла-метки типа FIFO для именованных pip’ов. Если на вашей машине функционируют X-Windows, то вы сможете обнаружить такой файл в директории с именем /tmp/.X11-unix – это файл типа "сокет", служащий для взаимодействия локальных процессов с оконным сервером.
Для хранения полного адреса сокета используется структура следующего вида, описанного в файле <sys/un.h>:
struct sockaddr_un{ short sun_family; /* Избранное семейство протоколов – всегда AF_UNIX */
char sun_path[108]; /* Имя файла типа "сокет" */ };
Выбранное имя файла мы будем копировать внутрь структуры, используя функцию strcpy().
Фактическая длина полного адреса сокета, хранящегося в структуре с именем my_addr, может быть вычислена следующим образом: sizeof(short)+strlen(my_addr.sun_path). В Linux для этих целей можно использовать специальный макрос языка С
SUN_LEN(struct sockaddr_un*)
Ниже приведены тексты переписанных под семейство UNIX Domain протоколов клиента и сервера для сервиса echo (программы 15–16-5.c и 15–16-6.c), общающиеся через датаграммы. Клиент использует сокет с именем AAAA в текущей директории, а сервер – сокет с именем BBBB. Как следует из описания типа данных, эти имена (полные или относительные) не должны по длине превышать 107 символов. Комментарии даны лишь для изменений по сравнению с программами 15–16-1.c и 15–16-2.c.
Листинг 15-16.5. Программа 15–16-5.c . A simple echo UNIX Domain datagram server (html, txt)
Листинг 15-16.6. Программа 15–16-6.c . A simple echo UNIX Domain datagram client. (html, txt)
Наберите программы, откомпилируйте их и убедитесь в работоспособности.
Листинг 15-16.5. Программа 15–16-5.c . A simple echo UNIX Domain datagram server
/* A simple echo UNIX Domain datagram client */ #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> /* Новый include-файл вместо netinet/in.h и arpa/inet.h */ #include <string.h> #include <stdio.h> #include <errno.h> #include <unistd.h> int main() /* Аргументы командной строки не нужны, так как сервис является локальным, и не нужно указывать, к какой машине мы обращаемся с запросом */ { int sockfd; int n, len; char sendline[1000], recvline[1000]; struct sockaddr_un servaddr, cliaddr; /* новый тип данных под адреса сокетов */ if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) /* Изменен тип семейства протоколов */ { perror(NULL); exit(1); } bzero(&cliaddr, sizeof(cliaddr)); cliaddr.sun_family= AF_UNIX; /* Изменен тип семейства протоколов и имя поля в структуре */ strcpy(cliaddr.sun_path,"AAAA");/* Локальный адрес сокета клиента – AAAA – в текущей директории */ if(bind(sockfd, (struct sockaddr *) &cliaddr, SUN_LEN(&cliaddr)) < 0) /* Изменено вычисление фактической длины адреса */ { perror(NULL); close(sockfd); exit(1); } bzero(&servaddr, sizeof(servaddr)); servaddr.sun_family = AF_UNIX; /* Изменен тип семейства протоколов и имя поля в структуре */ strcpy(servaddr.sun_path,"BBBB"); /* Локальный адрес сокета сервера – BBBB – в текущей директории */ printf("String => "); fgets(sendline, 1000, stdin); if(sendto(sockfd, sendline, strlen(sendline)+1, 0, (struct sockaddr *) &servaddr, SUN_LEN(&servaddr)) < 0) /* Изменено вычисление фактической длины адреса */ { perror(NULL); close(sockfd); exit(1); } if((n = recvfrom(sockfd, recvline, 1000, 0, (struct sockaddr *) NULL, NULL)) < 0){ perror(NULL); close(sockfd); exit(1); } recvline[n] = 0; printf("%s", recvline); close(sockfd); return 0; }
Листинг 15-16.6. Программа 15–16-6.c . A simple echo UNIX Domain datagram client.
Наберите программы, откомпилируйте их и убедитесь в работоспособности.