В листинге 6.3 показана первая половина этой версии сервера.

Листинг 6.3. Сервер TCP, использующий одиночный процесс и функцию select: инициализация

//tcpcliserv/tcpservselect01.c

 1 #include "unp.h"

 2 int

 3 main(int argc, char **argv)

 4 {

 5  int i, maxi, maxfd, listenfd, connfd, sockfd;

 6  int nready, client[FD_SETSIZE],

 7  ssize_t n;

 8  fd_set rset, allset;

 9  char buf[MAXLINE];

10  socklen_t clilen;

11  struct sockaddr_in cliaddr, servaddr;

12  listenfd = Socket(AF_INET, SOCK_STREAM, 0);

13  bzero(&servaddr, sizeof(servaddr));

14  servaddr.sin_family = AF_INET;

15  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

16  servaddr.sin_port = htons(SERV_PORT);

17  Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));

18  Listen(listenfd, LISTENQ);

19  maxfd = listenfd; /* инициализация */

20  maxi = -1; /* индекс в массиве client[] */

21  for (i = 0; i < FD_SETSIZE; i++)

22   client[i] = -1; /* -1 означает свободный элемент */

23  FD_ZERO(&allset);

24  FD_SET(listenfd, &allset);

Создание прослушиваемого сокета и инициализация функции select

12-24 Этапы создания прослушиваемого сокета те же, что и раньше: вызов функций socket, bind и listen. Мы инициализируем структуры данных при том условии, что единственный дескриптор, который мы с помощью функции select выберем, изначально является прослушиваемым сокетом.

Вторая половина функции main показана в листинге 6.4.

Листинг 6.4. Сервер TCP, использующей одиночный процесс и функцию select: цикл

//tcpcliserv/tcpservselect01.c

25  for (;;) {

26   rset = allset; /* присваивание значения структуре */

27   nready = Select(maxfd + 1, &rset, NULL, NULL, NULL);

28   if (FD_ISSET(listenfd, &rset)) { /* соединение с новым клиентом */

29    clilen = sizeof(cliaddr);

30    connfd = Accept(listenfd, (SA*)&cliaddr, &clilen);

31    for (i = 0; i < FD_SETSIZE; i++)

32     if (client[i] < 0) {

33      client[i] = connfd; /* сохраняем дескриптор */

34      break;

35     }

36    if (i == FD_SETSIZE)

37     err_quit("too many clients");

38    FD_SET(connfd, &allset); /* добавление нового дескриптора */

39    if (connfd > maxfd)

40     maxfd = connfd; /* для функции select */

41    if (i > maxi)

42     maxi = i; /* максимальный индекс в массиве clientf[] */

43    if (--nready <= 0)

44     continue; /* больше нет дескрипторов, готовых для чтения */

45   }

46   for (i = 0; i <= maxi; i++) { /* проверяем все клиенты на наличие

                                      данных */

47    if ((sockfd - client[i]) < 0)

48     continue;

49    if (FD_ISSET(sockfd, &rset)) {

50     if ((n = Read(sockfd, buf, MAXLINE)) == 0) {

51      /* соединение закрыто клиентом */

52      Close(sockfd);

53      FD_CLR(sockfd, &allset);

54      client[i] = -1;

55     } else

56      Writen(sockfd, line, n);

57     if (--nready <= 0)

58      break; /* больше нет дескрипторов, готовых для чтения */

59    }

60   }

61  }

62 }

Блокирование в функции select

26-27 Функция select ждет, пока не будет установлено новое клиентское соединение или на существующем соединении не прибудут данные, сегмент FIN или сегмент RST.

Принятие новых соединений с помощью функции accept

28-45 Если прослушиваемый сокет готов для чтения, новое соединение установлено. Мы вызываем функцию accept и соответствующим образом обновляем наши структуры данных. Для записи присоединенного сокета мы используем первый незадействованный элемент массива client. Число готовых дескрипторов уменьшается, и если оно равно нулю, мы можем не выполнять следующий цикл for. Это позволяет нам использовать значение, возвращаемое функцией select, чтобы избежать проверки не готовых дескрипторов.

Проверка существующих соединений

46-60 Каждое существующее клиентское соединение проверяется на предмет того, содержится ли его дескриптор в наборе дескрипторов, возвращаемом функцией select. Если да, то из этого дескриптора считывается строка, присланная клиентом, и отражается обратно клиенту. Если клиент закрывает соединение, функция read возвращает нуль и мы обновляем структуры соответствующим образом.


Перейти на страницу:
Изменить размер шрифта: