UNIX: разработка сетевых приложений
Часть 70 из 399 Информация о книге
В листинге 6.3 показана первая половина этой версии сервера.
Листинг 6.3. Сервер TCP, использующий одиночный процесс и функцию select: инициализация
//tcpcliserv/tcpservselect01.c1 #include "unp.h"2 int3 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Вторая половина функции
показана в листинге 6.4.mainЛистинг 6.4. Сервер TCP, использующей одиночный процесс и функцию select: цикл
//tcpcliserv/tcpservselect01.c25 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 } else56 Writen(sockfd, line, n);57 if (--nready <= 0)58 break; /* больше нет дескрипторов, готовых для чтения */59 }60 }61 }62 }Блокирование в функции selectФункция26-27ждет, пока не будет установлено новое клиентское соединение или на существующем соединении не прибудут данные, сегмент FIN или сегмент RST.selectПринятие новых соединений с помощью функции acceptЕсли прослушиваемый сокет готов для чтения, новое соединение установлено. Мы вызываем функцию28-45и соответствующим образом обновляем наши структуры данных. Для записи присоединенного сокета мы используем первый незадействованный элемент массиваaccept. Число готовых дескрипторов уменьшается, и если оно равно нулю, мы можем не выполнять следующий циклclient. Это позволяет нам использовать значение, возвращаемое функциейfor, чтобы избежать проверки не готовых дескрипторов.selectПроверка существующих соединенийКаждое существующее клиентское соединение проверяется на предмет того, содержится ли его дескриптор в наборе дескрипторов, возвращаемом функцией46-60. Если да, то из этого дескриптора считывается строка, присланная клиентом, и отражается обратно клиенту. Если клиент закрывает соединение, функция read возвращает нуль и мы обновляем структуры соответствующим образом.select