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

ЖАНРЫ

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

2. Листинг 6.1 содержит следующую, модифицированную версию клиента. С помощью функции

select
клиент получает информацию о событиях в сети во время ожидания ввода пользователя. Однако проблема этой версии заключается в том, что программа не способна корректно работать в пакетном режиме. В листинге 6.2 эта проблема решается путем применения функции
shutdown
.

3. С листинга 16.1 начинается рассмотрение клиентов, использующих неблокируемый ввод-вывод.

4. Первым из рассмотренных нами клиентов, вышедшим за пределы ограничений, связанных с наличием единственного

процесса или потока для обслуживания всех запросов, является клиент, изображенный в листинге 16.6. В этом случае использовалась функция
fork
, и один процесс обрабатывал передачу данных от клиента к серверу, а другой — в обратном направлении.

5. В листинге 26.1 используются два потока вместо двух процессов.

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

30.3. Тестовый клиент TCP

В листинге 30.1 [1] показан клиент, который будет использоваться для тестирования всех вариаций нашего сервера.

Листинг 30.1. Код клиента TCP для проверки различных версий сервера

//server/client.с

1 #include "unp.h"

2 #define MAXN 16384 /* максимальное количество байтов, которые могут быть

запрошены клиентом от сервера */

1

Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.

3 int

4 main(int argc, char **argv)

5 {

6 int i, j, fd, nchildren, nloops, nbytes;

7 pid_t pid;

8 ssize_t n,

9 char request[MAXLINE], reply[MAXN];

10 if (argc != 6)

11 err_quit("usage: client <hostname or IPaddr> <port> <#children> "

12 "<#loops/child> <#bytes/request>");

13 nchildren = atoi(argv[3]);

14 nloops = atoi(argv[4]);

15 nbytes = atoi(argv[5]);

16 snprintf(request, sizeof(request), "%d\n", nbytes); /* в конце

символ новой строки */

17 for (i = 0; i < nchildren; i++) {

18 if ((pid = Fork) == 0) { /* дочерний процесс */

19 for (j = 0; j < nloops; j++) {

20 fd = Tcp_connect(argv[1], argv[2]);

21 Write(fd, request, strlen(request));

22 if ((n = Readn(fd, reply, nbytes)) != nbytes)

23 err_quit("server returned %d bytes", n);

24 Close(fd); /*
состояние TIME_WAIT на стороне клиента,

а не сервера */

25 }

26 printf("child %d done\n", i);

27 exit(0);

28 }

29 /* родительский процесс снова вызывает функцию fork */

30 }

31 while (wait(NULL) > 0) /* теперь родитель ждет завершения всех

дочерних процессов */

32 ;

33 if (errno != ECHILD)

34 err_sys("wait error");

35 exit(0);

36 }

10-12
Каждый раз при запуске клиента мы задаем имя узла или IP-адрес сервера, порт сервера, количество дочерних процессов, порождаемых функцией
fork
(что позволяет нам инициировать несколько одновременных соединений с сервером), количество запросов, которое каждый дочерний процесс должен посылать серверу, и количество байтов, отправляемых сервером в ответ на каждый запрос.

17-30
Родительский процесс вызывает функцию
fork
для порождения каждого дочернего процесса, и каждый дочерний процесс устанавливает указанное количество соединений с сервером. По каждому соединению дочерний процесс посылает запрос, задавая количество байтов, которое должен вернуть сервер, а затем дочерний процесс считывает это количество данных с сервера. Родительский процесс просто ждет завершения выполнения всех дочерних процессов. Обратите внимание, что клиент закрывает каждое соединение TCP, таким образом состояние TCP TIME_WAIT имеет место на стороне клиента, а не на стороне сервера. Это отличает наше клиент-серверное соединение от обычного соединения HTTP.

При тестировании различных серверов из этой главы мы запускали клиент следующим образом:

% client 192.168.1.20 8888 5 500 4000

Таким образом создается 2500 соединений TCP с сервером: по 500 соединений от каждого из 5 дочерних процессов. По каждому соединению от клиента к серверу посылается 5 байт (

"4000\n"
), а от сервера клиенту передается 4000 байт. Мы запускаем клиент на двух различных узлах, соединяясь с одним и тем же сервером, что дает в сумме 5000 соединений TCP, причем максимальное количество одновременных соединений с сервером в любой момент времени равно 10.

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