Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
5.4. Эхо-клиент TCP: функция main
В листинге 5.3 показана функция main
TCP-клиента.
Листинг 5.3. Эхо-клиент TCP
//tcpcliserv/tcpcli01.c
1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4 {
5 int sockfd;
6 struct sockaddr_in servaddr;
7 if (argc != 2)
8 err_quit("usage: tcpcli ");
9 sockfd = Socket(AF_INET, SOCK_STREAM, 0);
10 bzero(&servaddr. sizeof(servaddr));
11 servaddr.sin_family = AF_INET;
12 servaddr.sin_port = htons(SERV_PORT);
13 Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
14 Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));
15 str_cli(stdin, sockfd); /* эта функция выполняет все необходимые
действия со стороны клиента */
16 exit(0);
17 }
9-13
Создается сокет TCP и структура адреса сокета заполняется IP-адресом сервера и номером порта. IP-адрес сервера мы берем из командной строки, а известный номер порта сервера ( SERV_PORT
) — из нашего заголовочного файла unp.h
.
14-15
Функция connect
устанавливает соединение с сервером. Затем функция str_cli
(см. листинг 5.4) выполняет все необходимые действия со стороны клиента.
5.5. Эхо-клиент TCP: функция str_cli
Эта функция, показанная в листинге 5.4, обеспечивает отправку запроса клиента и прием ответа сервера в цикле. Функция считывает строку текста из стандартного потока ввода, отправляет ее серверу и считывает отраженный ответ сервера, после чего помещает отраженную строку в стандартный поток вывода.
Листинг 5.4. Функция str_cli: цикл формирования запроса клиента
//lib/str_cli.c
1 #include "unp.h"
2 void
3 str_cli(FILE *fp, int sockfd)
4 {
5 char sendline[MAXLINE], recvline[MAXLINE];
6 while (Fgets(sendline, MAXLINE, fp) != NULL) {
7 Writen(sockfd,. sendline, strlen(sendline));
8 if (Readline(sockfd, recvline, MAXLINE) == 0)
9 err_quit("str_cli: server terminated prematurely");
10 Fputs(recvline, stdout);
11 }
12 }
6-7
Функция fgets
считывает строку текста, а функция writen
отправляет эту строку серверу.
8-10
Функция readline
принимает отраженную сервером строку, а функция fputs
записывает ее в стандартный поток вывода.
11-12
Цикл завершается, когда функция fgets
возвращает пустой указатель, что означает достижение конца файла или обнаружение ошибки. Наша функция-обертка Fgets
проверяет наличие ошибки, и если ошибка действительно произошла, прерывает выполнение программы. Таким образом, функция Fgets
возвращает пустой указатель только при достижении конца файла.
5.6. Нормальный запуск
Наш небольшой пример использования TCP (около 150 строк кода для двух функций main
, str_echo
, str_cli
, readline
и writen
) позволяет понять, как запускаются и завершаются клиент и сервер и, что наиболее важно, как развиваются события, если произошел сбой на узле клиента или в клиентском процессе, потеряна связь в сети и т.д. Только при понимании этих «граничных условий» и их взаимодействия с протоколами TCP/IP мы сможем обеспечить устойчивость клиентов и серверов, которые смогут справляться с подобными ситуациями.
Сначала мы запускаем сервер в фоновом режиме на узле linux
.
linux % tcpserv01 &
[1] 17870
Когда сервер запускается, он вызывает функции socket
, bind
, listen
и accept
, а затем блокируется в вызове функции accept
. (Мы еще не запустили клиент.) Перед тем, как запустить клиент, мы запускаем программу netstat
, чтобы проверить состояние прослушиваемого сокета сервера.
linux % netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 *:9877 *:* LISTEN
Здесь мы показываем только первую строку вывода и интересующую нас строку. Эта команда показывает состояние всех сокетов в системе, поэтому вывод может быть большим. Для просмотра прослушиваемых сокетов следует указать параметр -a
.
Результат совпадает с нашими ожиданиями. Сокет находится в состоянии LISTEN, локальный IP-адрес задан с помощью символа подстановки (то есть является универсальным) и указан локальный порт 9877. Функция netstat
выводит звездочку для нулевого IP-адреса ( INADDR_ANY
, универсальный адрес) или для нулевого порта.
Затем на том же узле мы запускаем клиент, задав IP-адрес сервера 127.0.0.1. Мы могли бы задать здесь и нормальный адрес сервера (его IP-адрес в сети).
linux % tcpcli01 127.0.0.1
Клиент вызывает функции socket
и connect
, последняя осуществляет трехэтапное рукопожатие TCP. Когда рукопожатие TCP завершается, функция connect возвращает управление процессу-клиенту, а функция accept
— процессу-серверу. Соединение установлено. Затем выполняются следующие шаги:
1. Клиент вызывает функцию str_cli
, которая блокируется в вызове функции fgets
, поскольку мы еще ничего не ввели.
2. Когда функция accept
возвращает управление процессу-серверу, последний вызывает функцию fork
, а дочерний процесс вызывает функцию str_echo
. Та вызывает функцию read
, блокируемую в ожидании получения данных от клиента.
3. Родительский процесс сервера снова вызывает функцию accept
и блокируется в ожидании подключения следующего клиента.
У нас имеется три процесса, и все они находятся в состоянии ожидания (блокированы): клиент, родительский процесс сервера и дочерний процесс сервера.
Мы специально поставили первым пунктом (после завершения трехэтапного рукопожатия) вызов функции str_cli, происходящий на стороне клиента, а затем уже перечислили действия на стороне сервера. Причину объясняет рис. 2.5: функция connect возвращает управление, когда клиент получает второй сегмент рукопожатия. Однако функция accept не возвращает управление до тех пор, пока сервер не получит третий сегмент рукопожатия, то есть пока не пройдет половина периода RTT после завершения функции connect.
Мы намеренно запускаем и клиент, и сервер на одном узле — так проще всего экспериментировать с клиент-серверными приложениями. Поскольку клиент и сервер запущены на одном узле, функция netstat
отображает теперь две дополнительные строки вывода, соответствующие соединению TCP:
l inux % netstat -a
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 localhost:9877 localhost:42758 ESTABLISHED
tcp 0 0 localhost:42758 localhost:42758 ESTABLISHED
tcp 0 0 *:9877 *:* LISTEN
Первая из строк состояния ESTABLISHED
соответствует дочернему сокету сервера, поскольку локальным портом является порт 9877. Вторая строка ESTABLISHED
— это клиентский сокет, поскольку локальный порт — порт 42 758. Если мы запускаем клиент и сервер на разных узлах, на узле клиента будет отображаться только клиентский сокет, а на узле сервера — два серверных сокета.
Интервал:
Закладка: