Добавил:
Кафедра ВТ Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

lab11

.docx
Скачиваний:
10
Добавлен:
02.01.2023
Размер:
173.17 Кб
Скачать

МИНОБРНАУКИ РОССИИ

Санкт-Петербургский государственный

электротехнический университет

«ЛЭТИ» им. В.И. Ульянова (Ленина)

Кафедра вычислительной техники

Отчет по лабораторной работе №11

по дисциплине «Организация процессов и программирование в среде Linux»

Студент гр.

Преподаватель

Разумовский Г.В.

Тема: Взаимодействие процессов

через сокеты

Цель работы: знакомство с механизмом взаимодействия процессов через сокеты.

Задание:

Написать две программы (сервер и клиент) , которые обмениваются сообщениями через потоковые сокеты. Клиенты проверяют возможность соединения с сервером и в случае отсутствия соединения или истечения времени ожидания отправки сообщения завершают работу. После соединения с сервером они генерируют случайную последовательность чисел и выводят ее на экран, а затем отсылают серверу. Сервер в течение определенного времени ждет запросы от клиентов и в случае их отсутствия завершает работу. При поступлении запроса от клиента сервер порождает обслуживающий процесс, который принимает последовательность чисел, упорядочивает ее и выводит на экран, а затем отсылает обратно клиенту и завершают работу. Клиент полученную последовательность выводит на экран и заканчивает свою работу.

Ход работы

Программа server.cpp

//может быть только 1 сервер

#include <iostream>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

using namespace std;

//Вопросы от Разумовского:

//какие параметры всего? сколько времени я жду перед тем, как включиться?

int main(int argc, char* argv[])

{

    if (argc != 2){

        cout << "Должно быть введено время ожидания: ./server {sec}\n";

        exit(0);

    }

    // Инициализация и подготовка

    int socket_fd; // дескриптор сокета-приемника, получающего сокет, желающий установить соединение с сервером

    int socket_listener; //  сокет-"слушатель", который слушает все запросы к серверу

    char socket_client_name[255];

    fd_set fds; // набор дескрипторов

    pid_t process_id; // id нового процесса

    sockaddr_in socket_address_in; //структура с адресом сервера (IP)

    timeval time_value; // структура с максимальным временем ожидания послания сообщения

    const int MORBING_TIME = atoi(argv[1]);

   

    // ---------- Создание сокета ----------

    // создание сокета-слушателя, который принимает все запросы к серверу

    // идет установка порта, протокола IPv4 и адреса

    // затем привязать к ним настройки(порта, протокола IPv4 и адреса)

    // чтобы остальные могли идентифицировать его сейчас (до этого у нас не было точного его адреса,

    // и остальные не могли знать, как к нему подключиться)

   

    socket_listener = socket(AF_INET, SOCK_STREAM, 0); // создание принимающего сокета

    if(socket_listener < 0)

    {

        perror("Ошибка создания сокета");

        exit(1);

    }

   

    socket_address_in.sin_family = AF_INET; // сетевое взаимодействие по протоколу IPv4

    socket_address_in.sin_port = htons(3434); // номер порта в сетевом порядке байт

    socket_address_in.sin_addr.s_addr = inet_addr("127.0.0.1"); // "127.0.0.1" - это "localhost"

    //Привязка сокета к IP-адресу и номеру порта

    if(bind(socket_listener, (sockaddr*)&socket_address_in, sizeof(socket_address_in)) < 0) // привязка к сетевому адресу

    {

        perror("Ошибка соединения с сетью");

        exit(2);

    }

   

    // ---------- Получение сообщения от клиентов и отправка к ним сообщения ----------

    // идет установка тайм-аута для ожидания любых запросов от клиентов,

    // каждый раз когда мы устанавливаем новое соединение, тайм-аут устанавливается в 0,

    // отсчитываем 15 секунд,

    // затем устанавливаем максимальное кол-во соединений для сервера (длина очереди - 10),

    // а уже потом мы ожидаем какие-либо запросы от сокета-слушателя,  принимаем его с сокетом-принимателем

    // и запустить новый процесс, который обрабатывает сообщение и отправляет его в обратном направлении

     

    time_value.tv_sec = MORBING_TIME;

    time_value.tv_usec = 0;

   

    listen(socket_listener, 10); // создание очереди для привязанных сокетов

    while(true) // ожидание сокета

    {

        FD_ZERO(&fds); // очищаем набор дескрипторов

        FD_SET(socket_listener, &fds); // добавляем наши дескрипторы в набор

        time_value.tv_sec = MORBING_TIME; // это здесь нужно, т.к. потом он передается по указателю

        time_value.tv_usec = 0;

       

        //мониторинг файловых дескрипторов, чтобы найти готовые к операциям ввода-вывода, вернуть количество файловых дескрипторов, содержащихся в трех возвращенных наборах дескрипторов/0, если тайм-аут, если успех, -1, если ошибка

       

        // select(количество запрошенных дескрипторов, проверка готовности к чтению, время)

        // возвращает число сокетов, от которых поступила информация (если 0, время ожидания истекло)

        if(select(FD_SETSIZE, &fds, NULL, NULL, &time_value) == 0)

        {

            cout << "---------- Время ожидания запроса от клиента вышло ----------\n";

            close(socket_fd);

            exit(1);

        }

        else

        {

//          Возвращает дескриптор сокета для обмена с клиентом, или -1 в случае ошибки

            socket_fd = accept(socket_listener, NULL, NULL);

            if(socket_fd < 0)

            {

                perror("Ошибка установки соединения");

                exit(1);

            }

            sprintf(socket_client_name, "%d", socket_fd); // все агрументы execl должны быть const char*

            process_id = fork();

            if(process_id == 0)

                execl("executable", " ", socket_client_name, NULL);

        }

    }

   

    // ---------- Уничтожение ----------

    if (process_id != 0) // если это НЕ новый процесс, закрываем ЕГО сокет

        close(socket_listener);

   

    return 0;

}

Программа executable.cpp

// Обслуживающая программа для сервера

#include <iostream>

#include <sys/socket.h>

#include <netinet/in.h>

#include <unistd.h>

#include <algorithm>

using namespace std;

int main(int argc, char* argv[])

{

    // Подготовка

    int buffer_len; // размер локального буфера

    int local_socket_fd = atoi(argv[1]); // дескриптор клиентского сокета

    char subsequence_buf[10]; // локальный буфер для отправляемого сообщения

   

    // ---------- Получение сообщения от клиентов и последовательности обработчиков и отправка сообщения клиентам ----------

    // Читаем сообщение от клиента (которое было на сервере, но мы разветвили процесс, поэтому оно у нас есть),

    // затем мы сортируем цифры в нем,

    // после чего мы отправляем его обратно (мы получили клиентский адрес в качестве АРГУМЕНТА)

    // (откуда было получено сообщение, туда же его и отправили)

   

    timeval time_value;

    time_value.tv_sec = 5;

    //Если через 5 секунд подсервер не получит сообщение, то он прекращает работу

    //Программа не будет прекращаться в ином случае (ошибка сообщения и прочее)

    time_value.tv_usec = 0;

    setsockopt(local_socket_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&time_value, sizeof(time_value)); // установка параметров для сокета-получателя

    buffer_len = recv(local_socket_fd, subsequence_buf, 10, 0); // получение необработанного сообщения

    cout << "---------- Сообщение \"" << subsequence_buf << "\" было получено ----------\n";

   

    // в третьем задании нам нужно получить последовательность цифр,

    // отсортировать ее, затем вернуть клиенту отсортированную последовательность

    // например, "4278600937" --> "0023467789"

    sort(subsequence_buf, subsequence_buf + sizeof(subsequence_buf));

   

    if (send(local_socket_fd, subsequence_buf, buffer_len, 0) > 0)

        cout << "---------- Сообщение \"" << subsequence_buf << "\" было отправлено ----------\n\n";

        //отправка обработанного сообщения (от клиента к серверу к подсерверу к клиенту)

        //Мы отправляем сразу клиенту по local_socket_fd (в обход сервера, так удобнее)

        //это - сокет сервера, который он принимает от клиента

    else cout << "---------- Ошибка отправки сообщения (клиенту) ----------\n\n";

   

    // ---------- Уничтожение ----------

    close(local_socket_fd);

    exit(0);

}

Программа client.cpp

//Клиентов может быть много

#include <iostream>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

using namespace std;

int main(int argc, char* argv[])

{

    if (argc != 2){

        cout << "Должно быть введено время ожидания: ./client {sec}\n";

        exit(0);

    }

    // Подготовка

    int socket_fd; // дескриптор сокета клиента

    int is_connected = 0; // подключен ли клиент к сокету

    int begin_time = 0; // время начала отчета

    char message_send[10]; // отправляемое сообщение от клиента к серверу

    char message_receive[10]; // обработанное полученное сообщение (от сервера к клиенту)

    fd_set readfds; // набор дескрипторов

    sockaddr_in socket_address_in; // структура с адресом сервера

    timeval time_value; // структура с максимальным временем ожидания отправки сообщения

    const int MORBING_TIME = atoi(argv[1]);

   

    // ---------- Создание сокета ----------

    // создается сокет, он будет отправлен на сервер

    // установка порта соединения, протокола IPv4 и адреса для находжения сервера

    // такие же настройки должны быть в сервере

   

    srand(time(NULL));

   

    /*

    создает сокет (домен, протокол), в случае успеха возвращает дескриптор файла для нового сокета

    AF_INET -- для взаимодействия через сеть по протоколу TCP/IP (IPv4)

    SOCK_STREAM -- потоковый сокет

    */

    socket_fd = socket(AF_INET, SOCK_STREAM, 0);

   

    if(socket_fd < 0)

    {

        perror("Ошибка создания сокета");

        exit(1);

    }

   

    socket_address_in.sin_family = AF_INET; // взаимодействие с сетью по протоколу IPv4

    socket_address_in.sin_port = htons(3434); // номер порта в сетевом порядке байт

    socket_address_in.sin_addr.s_addr = inet_addr("127.0.0.1"); // "127.0.0.1" - это "localhost"

   

    // ---------- Ожидание взаимодействия с сервером ----------

   

    begin_time = time(NULL);

    cout << "---------- Ожидание соединения с сервером (" << MORBING_TIME << " секунд) ----------\n";

   

    while ((time(NULL) - begin_time) < MORBING_TIME

    && (is_connected = connect(socket_fd, (sockaddr*)&socket_address_in, sizeof(socket_address_in))) < 0)

    {} // просто ждем 15 секунд, пока не будет соединения с сервером

   

    if (is_connected == -1)

    {

        cout << "---------- Не удалось соединиться с сервером ----------\n";

        close(socket_fd);

        exit(-1);

    }

   

    // ---------- Создание последовательности и отправка сообщения на сервер ----------

    // Создаем случайную последовательность из 10 цифр,

    // устанавливает время тайм-аута,

    // отправляем сообщение и выводим его в терминал

   

    for (int i = 0; i < 10; i++)

        message_send[i] = '0' + rand()%10;

    //sprintf(message_send, "%lu", rand()%9000000000 + 1000000000); // случайное число из 10 цифр

   

    // время ожидания

    time_value.tv_sec = MORBING_TIME;

    time_value.tv_usec = 0;

   

    /*

    установка параметров сокета

    SOL_SOCKET  -- задает параметры на уровне сокета

    SO_SNDTIMEO -- задает значение тайм-аута для сокета

    */

    setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&time_value, sizeof(time_value)); // задаем параметры сокета перед отправкой

   

    send(socket_fd, message_send, sizeof(message_send), 0); // отправка сообщения на сервер

   

    cout << "---------- Сообщение \"" << message_send <<"\" отправлено ----------\n";

   

    // ---------- Получение сообщения от сервера ----------

    // ожидаем запрос от сервера,

    // устанавливаем тайм-аут на получение сообщения и выводим полученное сообщение

   

    FD_ZERO(&readfds); // очищаем набор дескрипторов

    FD_SET(socket_fd, &readfds); // добавление дескриптора в набор

   

    time_value.tv_sec = MORBING_TIME;

    time_value.tv_usec = 0;

   

    // select(количество запрошенных дескрипторов, проверка готовности к чтению, время)

    // возвращает число сокетов, от которых поступила информация (если 0, время ожидания истекло)

    if(select(FD_SETSIZE, &readfds, NULL, NULL, &time_value) == 0)

        cout << "---------- Время ожидания вышло ----------\n";

    else

    {

        recv(socket_fd, message_receive, sizeof(message_receive), 0); //получение сообщения

        cout << "---------- Сообщение \"" << message_receive << "\" получено ----------\n";

    }

   

    // ---------- Уничтожение ----------

    close(socket_fd);

    return 0;

}

Демонстрация результата программы

Как видно из кода программы, если на вход серверу и клиенту не задать время ожидания, то программы запускаться не будут

Санкт-Петербург

2022

Соседние файлы в предмете Программирование в среде Linux