Московский физико-технический институт
Опубликован: 16.09.2004 | Доступ: свободный | Студентов: 7855 / 1653 | Оценка: 4.44 / 4.28 | Длительность: 15:33:00
Лекция 10:

Семейство протоколов TCP/IP. Сокеты (sockets) в UNIX и основы работы с ними

< Лекция 9 || Лекция 10: 1234567891011

Создание программы с параллельной обработкой запросов клиентов

В приведенном выше примере сервер осуществлял последовательную обработку запросов от разных клиентов. При таком подходе клиенты могут подолгу простаивать после установления соединения, ожидая обслуживания. Поэтому обычно применяется схема псевдопараллельной обработки запросов. После приема установленного соединения сервер порождает процесс-ребенок, которому и поручает дальнейшую работу с клиентом. Процесс-родитель закрывает присоединенный сокет и уходит на ожидание нового соединения. Схематично организация такого сервера изображена на рис. 15–16.9.

Напишите, откомпилируйте и запустите такой параллельный сервер. Убедитесь в его работоспособности. Не забудьте о необходимости удаления зомби-процессов.

Применение интерфейса сетевых вызовов для других семейств протоколов. UNIX Domain протоколы. Файлы типа "сокет"

Рассмотренный нами интерфейс умеет работать не только со стеком протоколов TCP/IP, но и с другими семействами протоколов. При этом требуется лишь незначительное изменение написанных с его помощью программ. Рассмотрим действия, которые необходимо выполнить для модернизации написанных для TCP/IP программ под другое семейство протоколов.

  1. Изменяется тип сокета, поэтому для его точной спецификации нужно задавать другие параметры в системном вызове socket() .
  2. В различных семействах протоколов применяются различные адресные пространства для удаленных и локальных адресов сокетов. Поэтому меняется состав структуры для хранения полного адреса сокета, название ее типа, наименования полей и способ их заполнения.
  3. Описание типов данных и предопределенных констант будет находиться в других include-файлах, поэтому потребуется заменить include-файлы <netinet/in.h> и <arpa/inet.h> на файлы, относящиеся к выбранному семейству протоколов.
  4. Может измениться способ вычисления фактической длины полного адреса сокета и указания его максимального размера.

И все!!!

Схема работы TCP-сервера с параллельной обработкой запросов

Рис. 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.

/* A simple echo UNIX Domain datagram server */
#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 clilen, n;
    char line[1000];
    struct sockaddr_un servaddr, cliaddr; /* новый 
        тип данных под адреса сокетов */
    if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) 
    /* Изменен тип семейства протоколов */
    {
        perror(NULL);
        exit(1);
    }
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sun_family = AF_UNIX; /* Изменен тип 
        семейства протоколов и имя поля в структуре */
    strcpy(servaddr.sun_path,"BBBB"); /* Локальный 
        адрес сокета сервера – BBBB – в текущей 
        директории */
    if(bind(sockfd, (struct sockaddr *) &servaddr, 
    SUN_LEN(&servaddr)) < 0) /* Изменено вычисление 
        фактической длины адреса */
    {
        perror(NULL);
        close(sockfd);
        exit(1);
    }
    while(1) {
        clilen = sizeof(struct sockaddr_un); /* Изменено 
            вычисление максимальной длины для адреса
            клиента */
        if((n = recvfrom(sockfd, line, 999, 0, 
        (struct sockaddr *) &cliaddr, &clilen)) < 0){
            perror(NULL);
            close(sockfd);
            exit(1);
        }
        if(sendto(sockfd, line, strlen(line), 0, 
        (struct sockaddr *) &cliaddr, clilen) < 0){
            perror(NULL);
            close(sockfd);
            exit(1);
        }
    }
    return 0;
}
Листинг 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.

Наберите программы, откомпилируйте их и убедитесь в работоспособности.

Создание потоковых клиента и сервера для стека UNIX Domain протоколов

По аналогии с программами в предыдущем примере модифицируйте тексты программ TCP клиента и сервера для сервиса echo (программа 15–16-3.c и программа 15–16-4.c ) для потокового общения в семействе UNIX Domain протоколов. Откомпилируйте их и убедитесь в правильном функционировании.

< Лекция 9 || Лекция 10: 1234567891011
лия логовина
лия логовина

организовать двустороннюю поочередную связь процесса-родителя и процесса-ребенка через pipe, используя для синхронизации сигналы sigusr1 и sigusr2.

Макар Оганесов
Макар Оганесов