Чтение онлайн

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:

Рис. 6.15. Сервер TCP после того как установлено соединение со вторым клиентом

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

Рис. 6.16. Структуры данных после того как установлено соединение со вторым клиентом

Далее мы предположим, что первый клиент завершает свое соединение. TCP-клиент

отправляет сегмент FIN, превращая тем самым дескриптор номер 4 на стороне сервера в готовый для чтения. Когда наш сервер считывает этот присоединенный сокет, функция
readline
возвращает нуль. Затем мы закрываем сокет, и соответственно изменяются наши структуры данных. Значение
client[0]
устанавливается в -1, а дескриптор 4 в наборе дескрипторов устанавливается в нуль. Это показано на рис. 6.17. Обратите внимание, что значение переменной
maxfd
не изменяется.

Рис. 6.17. Структуры данных после того как первый клиент разрывает соединение

Итак, по мере того как приходят клиенты, мы записываем дескриптор их присоединенного сокета в первый свободный элемент массива

client
(то есть в первый элемент со значением -1). Следует также добавить присоединенный сокет в набор дескрипторов для чтения. Переменная
maxi
— это наибольший используемый в данный момент индекс в массиве
client
, а переменная
maxfd
(плюс один) — это текущее значение первого аргумента функции select. Единственным ограничением на количество обслуживаемых сервером клиентов является минимальное из двух значений:
FD_SETSIZE
и максимального числа дескрипторов, которое допускается для данного процесса ядром (о чем мы говорили в конце раздела 6.3).

В листинге 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);

Поделиться с друзьями: