Выбрать главу

  }

5. После того как вы определили, что есть активность, можно выяснить, какой из дескрипторов активен, проверяя каждый из них по очереди с помощью макроса FD_ISSET:

  for (fd = 0; fd < FD_SETSIZE; fd++) {

   if (FD_ISSET(fd, &testfds)) {

6. Если зафиксирована активность на server_sockfd, это может быть запрос на новое соединение, и вы добавляете в множество дескрипторов соответствующий client_sockfd:

    if (fd == server_sockfd) {

     client_len = sizeof(client_address);

     client_sockfd = accept(server_sockfd,

      (struct sockaddr*)&client_address, &client_len);

     FD_SET(client_sockfd, &readfds);

     printf("adding client on fd %d\n", client_sockfd);

    }

Если активен не сервер, значит, активность проявляет клиент. Если получен close, клиент исчезает, и можно удалить его из множества дескрипторов. В противном случае вы "обслуживаете" клиента, как и в предыдущих примерах.

    else {

     ioctl(fd, FIONREAD, &nread);

     if (nread == 0) {

      close(fd);

      FD_CLR(fd, &readfds);

      printf("removing client on fd %d\n", fd);

     } else {

      read(fd, &ch, 1);

      sleep(5);

      printf("serving client on fd %d\n", fd);

      ch++;

      write(fd, &ch, 1);

     }

    }

   }

  }

 }

}

Примечание

В реальную программу было бы неплохо вставить переменную, содержащую наибольший подключенный номер fd (необязательно самый последний подключенный номер fd). Это помешает просмотру в цикле тысяч номеров fd, которые даже не подсоединены и потенциально не могут быть готовы к чтению. Мы пропустили этот фрагмент кода для краткости и простоты примера.

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

$ ./server5 &

[1] 26686

server waiting

$ ./client3 & ./client3 & ./client3 & ps x

[2] 26689

[3] 26690

adding client on fd 4

server waiting

[4] 26691

PID   TTY  STAT TIME COMMAND

26686 pts/1 S   0:00 ./server5

26689 pts/1 S   0:00 ./client3

26690 pts/1 S   0:00 ./client3

26691 pts/1 S   0:00 ./client3

26692 pts/1 R+  0:00 ps x

$ serving client on fd 4

server waiting

adding client on fd 5

server waiting

adding client on fd 6

char from server = В

serving client on fd 5

server waiting

removing client on fd 4

char from server = В

serving client on fd 6

server waiting

removing client on fd 5

server waiting

char from server = В

removing client on fd 6

server waiting

[2]  Done  ./client3

[3]- Done  ./client3

[4]+ Done  ./client3

Для полноты аналогии, упомянутой в начале главы, в табл. 15.5 приведены параллели между соединениями на базе сокетов и телефонными переговорами.

Таблица 15.5

Телефон Сетевые сокеты
Звонок в компанию по номеру 555-0828 Подключение к IP-адресу 127.0.0.1
Ответ на звонок секретаря приемной Установка соединения с remote host
Просьба соединить с финансовым отделом. Маршрутизация с помощью заданного порта (9734)
Ответ на звонок администратора финансового отдела Вызов select вернул управление серверу
Звонок переадресован свободному менеджеру по работе с корпоративными заказчиками Сервер вызывает accept, создавая новый сокет на добавочный номер 456

Дейтаграммы

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

Хорошим примером может служить сервис daytime, использованный ранее в программе getdate.c. Вы создаете сокет, выполняете соединение, читаете единственный ответ и разрываете соединение. Столько операций для простого получения даты!

Сервис daytime так же доступен с помощью UDP-соединений, применяющих дейтаграммы. Для того чтобы воспользоваться им, просто пошлите сервису одну дейтаграмму и получите в ответ единственную дейтаграмму, содержащую дату и время. Все просто.

Сервисы, предоставляемые по UDP-протоколу, применяются в тех случаях, когда клиенту нужно создать короткий запрос к серверу, и он ожидает единственный короткий ответ. Если стоимость времени процессора достаточно низкая, сервер способен обеспечить такой сервис, обрабатывая запросы клиентов по одному и разрешая операционной системе хранить очередь входящих запросов. Такой подход упрощает программирование сервера.