Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Параллельный сервер на С

10K
27 ноября 2007 года
ljevik
48 / / 02.10.2006
Привет! Пишу простой сервер на С. Для обслуживания клиентов использую FD_SET, select(). Возможно ли при такой архитектуре сделать параллельный сервер: тоесть чтобы в один момент времени обслуживалось несколько клиентов? Я предполагаю создание нового процесса (fork()).
240
27 ноября 2007 года
aks
2.5K / / 14.07.2006
Селектом можно обслуживать, хотя и не удобно. С соданием нового процесса проблем конечно нет, и когда то даже так делали, но это не очень хороший вариант. Процесс все же довольно тяжелая вещь для ОС.
Наверняка ваша ОС поддерживает многопоточность, а потому вместо создания нового процесса - создавайте новый поток (pthread_create и далее по документации) на новое подключение. Это довольно расспростроненная практика, плюс более удобное взаимодействие. Потоки паралельно (или псевдопаралельно) выполняются внутри одного процесса, имеют одно адресное пространство, общий доступ к динаимическим объектам и т.п. Не забывайте только о синхронизации.
10K
27 ноября 2007 года
ljevik
48 / / 02.10.2006
А про обслуживание селектом можно как-то поподробнее узнать?

Про новый трэд я думал, но у меня такой ужастный легаси код и его архитектура, что мне кажется, чтобы сделать новый трэд, мне нужно переписать половину сервера. Ведь там нужно вынести в отдельную функцию часть кода, это, конечно, не проблемма, но по-моему слишком много аргументов придется либо передавать, либо глобальными делать. А для процесса функции не нужно, насколько я понял, нужно только часть кода завернуть в if? Вот у меня и стоит проблемма - какую часть кода завернуть...Бьюсь уже неделю, может наметанный глаз профессионала сможет подсказать?

Код:
/* ...server`s set up was there ... */
listen(server_sockfd, 1);
        FD_ZERO(&readfds);
        FD_SET(server_sockfd, &readfds);
        FD_SET(0, &readfds);  /* Add keyboard to file descriptor set */
       
        char * testDir;
        size = pathconf(".", _PC_PATH_MAX);
        if ((testDir = (char *) malloc((size_t)size)) != NULL)//set server`s directory
            testDir = (char *) getcwd(testDir, (size_t)size);
        /*  Now wait for clients and requests */
        while (1) {
            testfds = readfds;
            select(FD_SETSIZE, &testfds, NULL, NULL, NULL);
                   
            /* If there is activity, find which descriptor it's on using FD_ISSET */
            for (fd = 0; fd < FD_SETSIZE; fd++) {
                if (FD_ISSET(fd, &testfds)) {
               
       
                    if (fd == server_sockfd) { /* Accept a new connection request */
                        client_sockfd = accept(server_sockfd, NULL, NULL);
                                             
                        if (num_clients < MAX_CLIENTS) {
                            FD_SET(client_sockfd, &readfds);
                            fd_array[num_clients] = client_sockfd;
                            clientInfo = malloc(sizeof(struct info));
                            clientInfo[num_clients]->clientFd = client_sockfd;
                            //strcpy(clientInfo[num_clients]->currentDir,testDir);
                            printf("Client %d joined\n In directory %s", num_clients++, clientInfo[num_clients]->currentDir);
                            fflush(stdout);
                        } else {
                            sprintf(msg, "XSorry, too many clients.  Try again later.\n");
                            write(client_sockfd, msg, strlen(msg));
                            close(client_sockfd);
                        }
                    } else if (fd) {  /*Process Client specific activity*/
В этой части идет чтение и обработка команд клиента, составление и отсылака запроса. Принятие и отсылака фаилов и т.д.
                    }


Спасибо за ответ!
240
28 ноября 2007 года
aks
2.5K / / 14.07.2006
Ну на самом деле не вижу принципиальной разницы при создании процесса и потока, кроме того что потоком как то правильнее, а процесс вобще перестает иметь доступ к данным парвого потока (только общение через ОС).

Передать в новый thread данные можно не обязательно глобальные. Можно их например динамически выделять, структурировать, а передавать указатель на эту структуру.

Про селект чуть позже постараюсь рассказать. там просто все псевдопаралеьность вручную надо делать )
256
28 ноября 2007 года
foxweb
1.0K / / 27.07.2005
Цитата: ljevik
Привет! Пишу простой сервер на С. Для обслуживания клиентов использую FD_SET, select(). Возможно ли при такой архитектуре сделать параллельный сервер: тоесть чтобы в один момент времени обслуживалось несколько клиентов? Я предполагаю создание нового процесса (fork()).



Есть именно такое решение. На 4м курсе делали многопоточный (не многопроцессный!) сервер-клиент: http://foxweb.net.ru/files/?id=225

342
28 ноября 2007 года
Yos
209 / / 21.06.2003
А лучше использовать "порты завершения" или "перекрытый ввод/вывод" там в первом как раз идеология потоков в полном объеме...

Хотя для простых реализаций обычный select подходит намного лучше, но его надо правильно использовать - использовать неблокирующие соединения и тд и тп...
10K
28 ноября 2007 года
ljevik
48 / / 02.10.2006
aks, акакую часть тогда в поток нужно выделить? Поидее ведь все, что после accept()? Но ведь у меня в разных условиях accept() и обслуживание пользователя. Я попробовал вынести ту часть которая обслуживает клиента (внутренности else if (fd) { }), не получилось:( по разному пробовал, сервер, то падает, то просто висит и ничего не делает.

foxweb, спасибо, но там код нужен, я не нахожусь не в одной из этих стран:)

Yos, спасибо за совет! А где-то можно про это почитать?
342
28 ноября 2007 года
Yos
209 / / 21.06.2003
http://www.piter.com/book.phtml?978531800725 - это лучшее!!! что существует на сегодняшний день.

Жаль только что в продаже вы ее не найдете...
240
28 ноября 2007 года
aks
2.5K / / 14.07.2006
Yos
Не был бы так категоричен. Тем более, что хотябы из-за fork(), автор програмирует не в сетях Windows. )
342
28 ноября 2007 года
Yos
209 / / 21.06.2003
Уважаемый AKS, я то собственно не очень то категоричен, так как вопрос то был несистемноориентированным, а технологические решения в большинстве своем одинаковые. А если искать именно системноориентированные решения то это может завести в ступор рано или поздно. У меня так было когда раньше писал для OS/2. Как только перестроился все пошло нормалек... Так сказать "патентный поиск" всегда дает результаты...

Вообщем предлагаю абстрагироваться от платформ и весь код давать на алгоритмическом языке Ершова, который плотненько так внедряли во всех школах на уроках информатики в конце 80-х годов :)))
240
28 ноября 2007 года
aks
2.5K / / 14.07.2006
А разве кто то тут речь вел про "системноориентированные решения"? ))
2
28 ноября 2007 года
squirL
5.6K / / 13.08.2003
вы против системноориентированных решений и рекомендуете книгу, описывающую сетевое программирование под Windows? оригинально, учитывая, что написание стевого кода под Windows довольно сильно отличается от написания кода для UNIX или Linux.
342
28 ноября 2007 года
Yos
209 / / 21.06.2003
Сразу вопрос - а Вы ее вообщето читали???

А по жизни - сокеты Беркли везде работают одинаково, select идет именно от туда из UNIX'а... Лучше сразу признайтесь что Вас просто коробит от Microsoft...

А так это в принципе должно выглядеть. А то от "пенсионеров" пока дождешся... Хотя ща налетят и будут говорить что принципиально работать не будет :)))

// Основной поток
main()
{
// инициализация и создание серверного сокета для соединения
...
ServerSocket = ;
...

listen();

// обработка подключений
while( true )
{
...
// выбираем только подключения на серверном сокете
FD_ZERO(&Read);
// добавляем серверный сокет в набор
FD_SET(ServerSocket,&Read);
...

select();

// есть что обрабатывать
if()
{
// соединяемся
ClientSocket = accept();

// создаем поток для обработки сообщений от клиента
new Thread(&Reader,ClientSocket);
}
}
}

// клиентский поток
Reader(Socket client)
{
// работаем с данными клиента
...

closesocket(client);
}
2
28 ноября 2007 года
squirL
5.6K / / 13.08.2003
Цитата: Yos
Сразу вопрос - а Вы ее вообщето читали???

А по жизни - сокеты Беркли везде работают одинаково



ага. только в винде не сокеты беркли. а своя реализация, ага?

10K
28 ноября 2007 года
ljevik
48 / / 02.10.2006
:( я в тупике...:( целый день просидел, ничего не получается...скажите, что не так:

Код:
while (1) {
            client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr,
                                                    &client_addr_len );
             if( (pid = fork()) < 0 ) {
                perror("fork");
                exit(EXIT_FAILURE);
            } else if(!pid) {
                char * testDir;
                size = pathconf(".", _PC_PATH_MAX);
                if ((testDir = (char *) malloc((size_t)size)) != NULL)//set server`s directory
                    testDir = (char *) getcwd(testDir, (size_t)size);
                mes = (char *) malloc(sizeof(char) * MSG_SIZE);
                result = read(client_sockfd, mes, MSG_SIZE);
                if (result == -1) {
                    perror("read");
                    exit(EXIT_FAILURE);
                }
                mes[result] = '\0';
                if (mes[0] == 'X') {
                    exitClient(client_sockfd, &readfds, fd_array, &num_clients);
                }
                printf("Message from client %d: %s\n", client_sockfd, mes);
                if (!strncmp(mes, "FBEGIN",6)) {
                    file = fopen ("SERVERblabla.txt", "w");  
                    char fileBuf[1];
                    recv(client_sockfd, buf, 4, 0) ;
                    while(buf[0] != -1) {
                        putc(buf[0], file);
                        recv(client_sockfd, buf, 4, 0) ;
                    }
                fclose(file);
                } else {
                    error = parseCommand(mes, responce, testDir);
                    if (error != -1)
                        if (error == 10) {
                            printf("doing get. Arg: %s\n", argument);
                            file = fopen(argument, "r");
                            if (!file) {
                                sprintf(responce, "Error while getting file <%s>. Check file name.\n", argument);
                                write(client_sockfd, responce, strlen(responce));
                            } else {
                                long fileSize;
                                fseek(file, 0, SEEK_END);  
                                fileSize = ftell(file);  
                                rewind(file);    
                                sprintf(responce, "FBEGIN:%s:%d\r\n", argument, fileSize);  
                                send(client_sockfd, responce, sizeof(responce), 0);
                                while ((i = getc(file)) != EOF){    
                                    buf[0] = i;    
                                    send(client_sockfd, buf, sizeof(int) / sizeof(char), 0);  
                                }    
                                buf[0] = -1;
                                send(client_sockfd, buf, sizeof(int) / sizeof(char), 0);
                                fclose(file);
                            }
                    } else {
                        write(client_sockfd, responce, error);    
                    }
                }


сейчас сервер вообще обрабатывает одну команду :confused: Причем если один юзер сменил директорию (testDir) командой cd, то почему-то другие клиенты попадают в его директорию. Ведь для них должен быть новый процесс с новой директорией!!!!! :confused: В чем дело????
342
29 ноября 2007 года
Yos
209 / / 21.06.2003
Цитата: squirL
только в винде не сокеты беркли. а своя реализация, ага?



а я и не спорю, что реализация разная, а вот то что они касательно модели select полностью совместимы это факт. Так что Ваша борьба за чистоту "нации" мне не очень интересна...

Лучше человеку помогите, а то напрашивается вывод что Вам нечего сказать по существу из-за отсутствия оного. Хотя скорее дело в другом, мы же не чета вам, набивавшим перфокарты (или начнете лить воду о том что они тоже разные?) с Буденным...

Вобщем пустую полемику лично я прекращаю...

256
29 ноября 2007 года
foxweb
1.0K / / 27.07.2005
Цитата: ljevik
foxweb, спасибо, но там код нужен, я не нахожусь не в одной из этих стран:)



Отписал код в личку, и по ошибке Yos :)

2
29 ноября 2007 года
squirL
5.6K / / 13.08.2003
ljevik
простите, некогда сейчас вчитываться в ваш код. каркас должен выглядеть так (это при классической модели, без select, poll и т. п.):
Код:
while (1) {
   newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilent);
   if (newsockfd < 0)
      error("ERROR on accept");
   pid = fork();
   if (pid < 0)
      error("ERROR on fork");
   if (pid == 0)  {
      close(sockfd);
      somefunction(newsockfd);
      exit(0);
   }
   else
   close(newsockfd);
}

т. е. - родитель, после fork - должен прибивать сокет и возвращаться к accept. потомок - вызывает некоторую функцию-обработчик, которая принимает параметром сокет и уже она начинает с ним работать и общаться с клиентами. у вас же - подозрительно много букв после ветвления в родителе. сократите код до минимума - чтобы работало правильно, а потом начинайте накручивать функционал. дома - посмотрю, если чадо кричать не будет над ухом :)
[QUOTE=Yos]а я и не спорю, что реализация разная, а вот то что они касательно модели select полностью совместимы это факт. Так что Ваша борьба за чистоту "нации" мне не очень интересна...[/QUOTE]
если бы вы внимательно читали книги, которые рекомендуете, вместо того, чтобы пытаться мне доказать, что я дурак - знали бы, что использование POSIX select в Windows - сродни забиванию гвоздей отверткой. в Windows используются Async расширения WSA.
[QUOTE=Yos]отя скорее дело в другом, мы же не чета вам, набивавшим перфокарты (или начнете лить воду о том что они тоже разные?) с Буденным...[/QUOTE]
хамство я расцениваю как знак того, что сказать по существу нечего именно вам.
10K
29 ноября 2007 года
ljevik
48 / / 02.10.2006
squirL, огромное спасибо! Ваш пример очень помог...теперь почти все работает. Только с отключение клиента проблеммы. если он набирает команду quit, то у меня на стороне клиента так:
 
Код:
if (strncmp(kb_msg, "quit", 4) == 0) {
                            sprintf(msg, "XClient is shutting down.\n");
                            write(sockfd, msg, strlen(msg));
                            close(sockfd); //close the current client
                            exit(0); //end program
                        }


а сервер, судя по всему продолжает считывать данные и приходит ошибка read: bad file descriptor..
...ну это я еще посмотрю. Просто на радостях решил написать...я с эти сервером уже месяц бьюсь!! :)

foxweb, и вам спасибо за доброту сердешую!:)
2
29 ноября 2007 года
squirL
5.6K / / 13.08.2003
чтобы не бится месяцами, я рекомендую вам не пожалеть денег и заказать две книги:
http://www.piter.com/book.phtml?978594723991
http://www.piter.com/book.phtml?978531800453
честное слово, не пожалеете :)
10K
02 декабря 2007 года
ljevik
48 / / 02.10.2006
спасибо за совет, но я Стивенса читаю "Юникс. Разработка сетевых приложений", вроде тоже её советовали.
А почему родитель и ребенок убиваю сокеты? Причем разные, что-то я не пойму?
2
02 декабря 2007 года
squirL
5.6K / / 13.08.2003
по первой ссылке именно она - свежее издание :-)

[QUOTE=ljevik]А почему родитель и ребенок убиваю сокеты? Причем разные, что-то я не пойму?[/QUOTE]
не совсем понял вопрос. поясните
10K
02 декабря 2007 года
ljevik
48 / / 02.10.2006
Для чего после создания нового процесса в родительском процессе и в дочернем нужном закрывать сокеты?
502
03 декабря 2007 года
Jail
550 / / 30.01.2007
Цитата:
Для чего после создания нового процесса в родительском процессе и в дочернем нужном закрывать сокеты?


Процесс потомок, как известно, порождаясь, приобретает всю структуру процесса родителя, кроме идентификатора процесса, получается как бы два одинаковых процесса (даже количество выделенной системной памяти под процессы одинаковое). Если существует идентификатор сокета (pid) в родителе, созданного до появления потомка, то его же и наследует потомок. Для этого и закрывают сокет в потомке, но думаю в родителе это лишнее.
Если используешь потоки (threads), то закрывать да открывать сокеты постоянно нет нужды. Данные процесса, породившего поток, прозрачны для потока во всем пространстве памяти процесса его родителя (как бы являются глобалными для потока). Так же потоки могут иметь свои личные данные (как бы локальные) маскируемые ключами.
Вобщем, сначало стоило бы почитать про потоки :)

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог