Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
5.17. Итоговый пример TCP
Прежде чем клиент и сервер TCP смогут взаимодействовать друг с другом, каждый из них должен определить пару сокетов для соединения: локальный IP-адрес, локальный порт, удаленный IP-адрес, удаленный порт. На рис. 5.5 мы схематически изображаем эти значения черными кружками. На этом рисунке ситуация представлена с точки зрения клиента. Удаленный IP-адрес и удаленный порт должны быть заданы клиентом при вызове функции connect
. Два локальных значения обычно выбираются ядром тоже при вызове функции connect
. У клиента есть выбор: он может задать только одно из локальных значений или оба, вызвав функцию bind
перед вызовом функции connect
, однако второй подход используется редко.

Рис. 5.5. TCP-соединение клиент-сервер с точки зрения клиента
Как мы отмечали в разделе 4.10, клиент может получить два локальных значения, выбранных ядром, вызвав функцию getsockname
после установления соединения.
На рис. 5.6 показаны те же четыре значения, но с точки зрения сервера.

Рис. 5.6. TCP-соединение клиент-сервер с точки зрения сервера
Локальный порт (заранее известный порт сервера) задается функцией bind
. Обычно сервер также задает в этом вызове универсальный IP-адрес, хотя может и ограничиться получением соединений, предназначенных для одного определенного локального интерфейса путем связывания с IP-адресом, записанным без символов подстановки (то есть не универсального). Если сервер связывается с универсальным IP-адресом на узле с несколькими сетевыми интерфейсами, он может определить локальный IP-адрес (указываемый как адрес отправителя в исходящих пакетах) при помощи вызова функции getsockname
после установления соединения (см. раздел 4.10). Два значения удаленного адреса возвращаются серверу при вызове функции accept. Как мы отмечали в разделе 4.10, если сервером, вызывающим функцию accept, выполняется с помощью функции exec другая программа, то эта программа может вызвать функцию getpeername
, чтобы при необходимости определить IP-адрес и порт клиента.
5.18. Формат данных
В нашем примере сервер никогда не исследует запрос, который он получает от клиента. Сервер лишь читает все данные, включая символ перевода строки, и отправляет их обратно клиенту, отслеживая только разделитель строк. Это исключение, а не правило, так как обычно необходимо принимать во внимание формат данных, которыми обмениваются клиент и сервер.
Пример: передача текстовых строк между клиентом и сервером
Изменим наш сервер так, чтобы он, по-прежнему принимая текстовую строку от клиента, предполагал, что строка содержит два целых числа, разделенных пробелом, и возвращал сумму этих чисел. Функции main
наших клиента и сервера остаются прежними, как и функция str_cli
. Меняется только функция str_echo
, что мы показываем в листинге 5.11.
Листинг 5.11. Функция str_echo, суммирующая два числа
//tcpcliserv/str_echo08.c
1 #include "unp.h"
2 void
3 str_echo(int sockfd)
4 {
5 long arg1, arg2;
6 ssize_t n;
7 char line[MAXLINE];
8 for (;;) {
9 if ((n = Readline(sockfd, line, MAXLINE)) == 0)
10 return; /* соединение закрывается удаленным концом */
11 if (sscanf(line, "%ld%ld", &arg1, &arg2) == 2)
12 snprintf(line, sizeof(line), "%ld\n", arg1 + arg2);
13 else
14 snprintf(line, sizeof(line), "input error\n");
15 n = strlen(line);
16 Writen(sockfd, line, n);
17 }
18 }
11-14
Мы вызываем функцию sscanf
, чтобы преобразовать два аргумента из текстовых строк в целые числа типа long
, а затем функцию snprintf
для преобразования результата в текстовую строку.
Эти клиент и сервер работают корректно вне зависимости от порядка байтов на их узлах.
Пример: передача двоичных структур между клиентом и сервером
Теперь мы изменим код клиента и сервера, чтобы передавать через сокет не текстовые строки, а двоичные значения. Мы увидим, что клиент и сервер работают некорректно, когда они запущены на узлах с различным порядком байтов или на узлах с разными размерами целого типа long
(см. табл. 1.5).
Функции main
наших клиента и сервера не изменяются. Мы определяем одну структуру для двух аргументов, другую структуру для результата и помещаем оба определения в наш заголовочный файл sum.h
, представленный в листинге 5.12. В листинге 5.13 показана функция str_cli
.
Листинг 5.12. Заголовочный файл sum.h
//tcpcliserv/sum.h
1 struct args {
2 long arg1;
3 long arg2;
4 };
5 struct result {
6 long sum;
7 };
Листинг 5.13. Функция str_cli, отправляющая два двоичных целых числа серверу
//tcpcliserv/str_cli09.c
1 #include "unp.h"
2 #include "sum.h"
3 void
4 str_cli(FILE *fp, int sockfd)
5 {
6 char sendline[MAXLINE];
7 struct args args;
8 struct result result;
9 while (Fgets(sendline, MAXLINE, fp) != NULL) {
10 if (sscanf(sendline, "%ld%ld", &args.arg1, &args.arg2) != 2) {
11 printf("invalid input, %s", sendline);
12 continue;
13 }
14 Writen(sockfd, &args, sizeof(args));
15 if (Readn(sockfd, &result, sizeof(result)) == 0)
16 err_quit("str_cli: server terminated prematurely");
17 printf("%ld\n", result.sum);
18 }
19 }
10-14
Функция sscanf
преобразует два аргумента из текстовых строк в двоичные. Мы вызываем функцию writen
для отправки структуры серверу.
15-17
Мы вызываем функцию readn
для чтения ответа и выводим результат с помощью функции printf
.
В листинге 5.14 показана наша функция str_echo
.
Листинг 5.14. Функция str_echo, складывающая два двоичных целых числа
//tcpcliserv/str_echo09.c
1 #include "unp.h"
2 #include "sum.h"
3 void
4 str_echo(int sockfd)
5 {
6 ssize_t n;
7 struct args args;
8 struct result result;
9 for (;;) {
10 if ((n = Readn(sockfd, &args, sizeof(args))) == 0)
11 return; /* соединение закрыто удаленным концом */
12 result.sum = args.arg1 + args.arg2;
13 Writen(sockfd, &result, sizeof(result));
14 }
15 }
9-14
Мы считываем аргументы при помощи вызова функции readn
, вычисляем и запоминаем сумму и вызываем функцию writen
для отправки результирующей структуры обратно.
Если мы запустим клиент и сервер на двух машинах с аналогичной архитектурой, например на двух компьютерах SPARC, все будет работать нормально:
solaris % tcpcli09 12.106.32.254
11 22 мы вводим эти числа
33 а это ответ сервера
-11 -44
Интервал:
Закладка: