Модераторы: feodorv
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Использование функции fork(), Непонятна ее использование в листинге 
:(
    Опции темы
Alexey91
  Дата 21.4.2012, 16:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 195
Регистрация: 18.9.2008

Репутация: нет
Всего: нет



Здравствуйте!!

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

У меня два вопроса:

1. По коду где используется функция fork().
Зачем в ветки 'case 0' после создания дочернего процесса сразу же выполняется функция close(listener); - закрытие слушающего сокета?

2. Функция fork() создает дочерний процесс. Если смотреть википедию, то создается копия родительского процесса. Если читать маны, то написано создается копия сервера.
А сервер в данном случае это что? Процесс, который использует исполняемый код сервера?

 
Код

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main()
{
    int sock, listener;
    struct sockaddr_in addr;
    char buf[1024];
    int bytes_read;

    listener = socket(AF_INET, SOCK_STREAM, 0);
    if(listener < 0)
    {
        perror("socket");
        exit(1);
    }
    
    addr.sin_family = AF_INET;
    addr.sin_port = htons(3425);
    addr.sin_addr.s_addr = INADDR_ANY;
    if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind");
        exit(2);
    }

    listen(listener, 1);
    
    while(1)
    {
        sock = accept(listener, NULL, NULL);
        if(sock < 0)
        {
            perror("accept");
            exit(3);
        }
        
        switch(fork())
        {
        case -1:
            perror("fork");
            break;
            
        case 0:
            close(listener);
            while(1)
            {
                bytes_read = recv(sock, buf, 1024, 0);
                if(bytes_read <= 0) break;
                send(sock, buf, bytes_read, 0);
            }

            close(sock);
            _exit(0);
            
        default:
            close(sock);
        }
    }
    
    close(listener);

    return 0;
}

PM   Вверх
boostcoder
Дата 21.4.2012, 18:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 13
Всего: 110



потому что если fork() возвращает 0 - значит текущий процесс является процессом ребенком. и, следовательно, выполнять accept() не задача детей, а задача родителя.
так же, из доки ты можешь узнать, что при форке - ребенок наследует дескрипторы от родителя.

PM WWW   Вверх
Олег2005
Дата 23.4.2012, 20:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Завсегдатай
Сообщений: 421
Регистрация: 26.5.2005
Где: Рига Латвия

Репутация: 6
Всего: 11



Листинг 2.12 UNIX TCP-сервер
Код

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h>
#include <netinet/in.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/wait.h>    /* Для константы WNOHANG*/
#define MYPORT 3490     /* Порт, на который будет идти соединение с пользователем*/
#define BACKLOG 10     /* Сколько ожидающих подключений может быть в очереди */
int main()
   {
     int sd, newsd;    /* Слушаем на сокете sd, новое подключение на сокете newsd */
     struct sockaddr_in my_addr;    /* Серверная адресная информация */
     struct sockaddr_in their_addr;    /* Адресная информация запрашивающей стороны (клиента) */
int sin_size;
/* Создаем сокет, ориентированный на соединение, для домена Интернет */
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1) 
{    perror ("socket");
exit (1);
      }
       my_addr.sin_family = AF_INET;          /* В порядке байтов хоста */
       my_addr.sin_port = htons (MYPORT);      /* short, в порядке байтов сети */
/* Авто-заполнение IP-адресом серверного сетевого интерфейса */
my_addr.sin_addr.s_addr = INADDR_ANY; 
bzero(&(my_addr.sin_zero), 8);         /* Обнуляем остальную часть struct */
/* Связываем только-что созданный сокет с его локальным адресом */
if (bind(sd, (struct sockaddr *)&my_addr, sizeof (struct sockaddr)) == -1) 
    {
perror ("bind");
exit (1);
   }
/* Организуем очередь прослушивания сети на порту MYPORT */
if (listen (sd, BACKLOG) == -1) 
{
perror ("listen");
exit (1);
}
while(1)        /* Главный цикл accept()*/
{    
sin_size = sizeof (struct sockaddr_in);
if ((newsd = accept (sd, (struct sockaddr *)&their_addr, &sin_size)) == -1) 
    {  
    perror ("accept");
       continue;        /* Несмотря на неудачу, продолжаем прием*/
        }
/* Приняли запрос на соединение и принимаем решение ответить на него*/
printf (" сервер: Принял соединение от %s\n ", inet_ntoa (their_addr.sin_addr));
if(!fork ()) 
{    /* Мы находимся в дочернем порожденном процессе */
/* Потомок наследует все файловые дескрипторы родителя, а значит, и newsd */
/* Теперь таких сокетов два - в каждом процессе по одному */
/* Шлем клиенту 14 байтов */
if (send(newfd, " Привет, мир! \n", 14, 0) == -1)perror ("send");
close(newsd);        /* Закрываем сокет newsd  в порожденном процессе */
exit (0);        /* Завершаем передачу данных из процесса потомка */
}            /* Здесь заканчивается текст программы-потомка*/
close (newsd);        /* Закрываем сокет newsd – родитель в нем не нуждается */
while (waitpid (-1, NULL, WNOHANG) > 0);    /* Очищаем порожденные процессы */
}    /*Конец цикла accept()*/        
}                /* Завершаем процесс сервера */

Для читателя, не знакомого с тонкостями программирования для UNIX-систем, дадим некоторые пояснения к приведенному тексту программы. Единственным способом создания процесса в системе UNIX является использование системного вызова fork(). Функционально, новый (порожденный, child) процесс создается посредством копирования выполняемого (родительского, parent) процесса. Порожденный процесс-потомок наследует большую часть атрибутов своего родителя, и в частности, открытые файлы (порожденный процесс обладает копией дескрипторов файлов, открытых его родителем). Особый процесс init является общим предком по отношению ко всем остальным. Он инициализирует совокупность администраторов системы (демонов) и связывает процесс с каждым терминалом. Системный вызов exec() позволяет запустить выполнение новой программы в рамках текущего процесса. Порождающий процесс может быть синхронизирован с порожденными благодаря функции wait(). Последние сигнализируют о своем окончании посредством обращения exit(). 
Если порожденный процесс заканчивается раньше своего родителя, обработка данных производится следующим образом:
-    если процесс-родитель находился в состоянии ожидания, обеспеченного функцией wait(), он выходит из этого состояния;
-    если родителем не была задействована функция типа wait(), порожденный процесс остается в состоянии "зомби", т.е. он возвращает все ресурсы, приданные ему системой, но сохраняет свое местонахождение в таблице процессов до подачи сигнала о его окончании.
-    Если порождающий процесс закончился раньше своих преемников, приемным родителем последних становится процесс init().  
Главное теперь – понять, что после выполнения вызова fork() у нас "одновременно" работают 2 процесса, и первой командой родительской программы будет инструкция close(newsd), которую родитель, скорее всего, начнет выполнять гораздо раньше, чем потомок передаст данные клиенту. Затем родитель исполняет функцию
while (waitpid (-1, NULL, WNOHANG) > 0);
Возвращаемое значение этой функции – идентификатор завершенного процесса-потомка. Назначение этой инструкции – дождаться завершения потомка и не дать ему превратиться в зомби. Это можно сделать и при помощи системного вызова wait(), однако вызов waitpid() предпочтительней и вот почему. Во-первых, первым параметром можно задать конкретный идентификатор процесса pid. В данном случае это не делается, но первым параметром со значением –1 указывается, что нужно дождаться завершения первого дочернего процесса. Второе важное обстоятельство – это параметр WNOHANG. Этим параметром ядро оповещается, что не нужно блокироваться (ждать) в функции waitpid(), если у родителя на момент исполнения этой функции не будет ни одного завершенного процесса-потомка. В тоже время функция wait() блокирует исполнение родителя до появления первого завершенного процесса-потомка и возвращает его pid.
Второй параметр - NULL – указывает, что статусная информация о процессе-потомке нас не интересует.

PM MAIL WWW MSN   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.0422 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.