Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » UNIX: разработка сетевых приложений - Уильям Стивенс

UNIX: разработка сетевых приложений - Уильям Стивенс

Читать онлайн UNIX: разработка сетевых приложений - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 237 238 239 240 241 242 243 244 245 ... 263
Перейти на страницу:

solaris26 % tpi_daytime 192.3.4.5

tpi_connect2: T_DISCON_IND from conn (148)

На этот раз мы получаем ошибку EHOSTUNREACH. Различие в том, что в первый раз не было возвращено сообщение ICMP о недоступности узла, а во второй раз мы получили это сообщение.

Следующая функция, которую мы рассмотрим, — это tpi_read, показанная в листинге 31.5. Она считывает данные из потока.

Листинг 31.5. Функция tpi_read: считывание данных из потока

//streams/tpi_read.c

 1 #include "tpi_daytime.h"

 2 ssize_t

 3 tpi_read(int fd, void *buf, size_t len)

 4 {

 5  struct strbuf ctlbuf;

 6  struct strbuf datbuf;

 7  union T_primitives rcvbuf;

 8  int flags;

 9  ctlbuf maxlen = sizeof(union T_primitives);

10  ctlbuf.buf = (char*)&rcvbuf;

11  datbuf.maxlen = len;

12  datbuf.buf = buf;

13  datbuf.len = 0;

14  flags = 0;

15  Getmsg(fd, &ctlbuf, &datbuf, &flags);

16  if (ctlbuf.len >= (int)sizeof(long)) {

17   if (rcvbuf.type == T_DATA_IND)

18    return (datbuf.len);

19   else if (rcvbuf.type == T_ORDREL_IND)

20    return (0);

21   else

22    err_quit("tpi_read: unexpected type %d", rcvbuf.type);

23  } else if (ctlbuf.len == -1)

24   return (datbuf.len);

25  else

26   err_quit("tpi_read: bad length from getmsg");

27 }

Считывание управляющей информации и данных, обработка ответа

9-26 На этот раз мы вызываем функцию getmsg для считывания как данных, так и управляющей информации. Структура strbuf, предназначенная для данных, указывает на буфер вызывающего процесса. В потоке события могут развиваться по четырем различным сценариям.

■ Данные могут прибыть в виде сообщения M_DATA, и указанием на это является возвращенное значение длины управляющей информации, равное -1. Данные скопированы в буфер вызывающего процесса функцией getmsg, и функция просто возвращает длину этих данных.

■ Данные могут прибыть как сообщение T_DATA_IND, в этом случае управляющая информация будет содержаться в структуре T_data_ind:

struct T_data_ind {

 long PRIM_type; /* T_DATA_IND */

 long MORE_flag; /* еще данные */

};

Если возвращено такое сообщение, мы игнорируем поле MORE_flag (оно вообще не задается для таких протоколов, как TCP) и просто возвращаем длину данных, скопированных в буфер вызывающего процесса функцией getmsg.

■ Сообщение T_ORDREL_IND возвращается, если все данные получены и следующим элементом является сегмент FIN:

struct T_ordrel_ind {

 long PRIM_type; /* T_ORDREL_IND */

};

Это нормальное завершение. Мы просто возвращаем нулевое значение, указывая вызывающему процессу, что по соединению получен признак конца файла.

■ Сообщение T_DISCON_IND возвращается, если произошел разрыв соединения. Наша последняя функция — это tpi_close, показанная в листинге 31.6.

Листинг 31.6. Функция tpi_close: отправка запроса о завершении собеседнику

//streams/tpi_close.c

 1 #include "tpi_daytime.h"

 2 void

 3 tpi_close(int fd)

 4 {

 5  struct T_ordrel_req ordrel_req;

 6  struct strbuf ctlbuf;

 7  ordrel_req PRIM_type = T_ORDREL_REQ;

 8  ctlbuf.len = sizeof(struct T_ordrel_req);

 9  ctlbuf.buf = (char*)&ordrel_req;

10  Putmsg(fd, &ctlbuf, NULL, 0);

11  Close(fd);

12 }

Отправка запроса о завершении собеседнику

7-10 Мы формируем структуру T_ordrel_req:

struct T_ordrel_req {

 long PRIM_type; /* T_ORDREL_REQ */

};

и посылаем ее как сообщение M_PROTO с помощью функции putmsg. Это соответствует функции XTI t_sndrel.

Этот пример позволил нам почувствовать специфику TPI. Приложение посылает сообщения вниз по потоку (запросы), а поставщик посылает сообщения вверх по потоку (ответы). Некоторые обмены сообщений организованы согласно простому сценарию «запрос-ответ» (связывание локального адреса), в то время как остальные могут занять некоторое время (установление соединения), позволяя нам заняться чем-то другим в процессе ожидания ответа. Для знакомства с TPI мы выбрали этот пример (написание клиента TCP) из-за его относительной простоты. Если бы мы решили написать с использованием TPI TCP-сервер, обрабатывающий одновременно несколько соединений, это было бы гораздо сложнее.

ПРИМЕЧАНИЕ

Можно сравнить количество системных вызовов, необходимых для осуществления определенных сетевых операций, показанных в этой главе, в случае применения TPI и когда используется ядро, реализующее сокеты. Связывание с локальным адресом в случае TPI требует двух системных вызовов, но в случае сокетного ядра требуется только один вызов [128, с. 454]. Для установления соединения на блокируемом дескрипторе с использованием TPI требуется три системных вызова, а в случае сокетного ядра — только один [128, с. 466].

31.7. Резюме

Иногда сокеты реализуются с использованием потоков STREAMS. Для обеспечения доступа к потоковой подсистеме вводятся четыре новые функции: getmsg, putmsg, getpmsg и putpmsg. Также в потоковой подсистеме широко используется уже описанная ранее функция ioctl.

TPI представляет собой потоковый интерфейс системы SVR4, предоставляющий доступ из верхних уровней на транспортный уровень. Он используется как сокетами, так и XTI, как показано на рис. 31.3. В этой главе в качестве примера использования основанного на сообщениях интерфейса мы разработали версию клиента времени и даты, в котором непосредственно применяется интерфейс TPI.

Упражнения

1. В листинге 31.6 мы вызываем функцию putmsg, чтобы отправить вниз по потоку запрос на нормальное завершение соединения, а затем немедленно вызываем функцию close для закрытия потока. Что произойдет, если наш запрос будет потерян потоковой подсистемой, а мы закроем поток?

Приложения

Приложение А

Протоколы IPv4, IPv6, ICMPv4 и ICMFV6

А.1. Введение

В этом приложении приведен обзор протоколов IPv4, IPv6, ICMPv4 и ICMPv6. Данный материал позволяет глубже понять рассмотренные в главе 2 протоколы TCP и UDP. Некоторые возможности IP и ICMP рассматриваются также более подробно и в других главах, например параметры IP (см. главу 27), и программы ping и traceroute (см. главу 28).

А.2. Заголовок IPv4

Уровень IP обеспечивает не ориентированную на установление соединения (connectionless) и ненадежную службу доставки дейтаграмм (RFC 791 [94]). Уровень IP делает все возможное для доставки IP-дейтаграммы определенному адресату, но не гарантирует, что дейтаграмма будет доставлена, прибудет в нужном порядке относительно других пакетов, а также будет доставлена в единственном экземпляре. Если требуется надежная доставка дейтаграммы, она должна быть обеспечена на более высоком уровне. В случае приложений TCP и SCTP надежность обеспечивается транспортным уровнем. Приложению UDP надежность должно обеспечивать само приложение, поскольку уровень UDP также не предоставляет гарантии надежной доставки дейтаграмм, что было показано на примере в разделе 22.5.

Одной из наиболее важных функций уровня IP является маршрутизация (routing). Каждая IP-дейтаграмма содержит адрес отправителя и адрес получателя. На рис. А.1 показан формат заголовка Ipv4.

Рис. А.1. Формат заголовка IPv4

■ Значение 4-разрядного поля версия (version) равно 4. Это версия протокола IP, используемая с начала 80-х.

■ В поле длина заголовка (header length) указывается полная длина IP-заголовка, включающая любые параметры, описанные 32-разрядными словами. Максимальное значение этого 4-разрядного поля равно 15, и это значение задает максимальную длину IP-заголовка 60 байт. Таким образом, если заголовок занимает фиксированные 20 байт, то 40 байт остается на различные параметры.

■ 16-разрядное поле кода дифференцированных сервисов (Differentiated Services Code Point, DSCP) (RFC 2474 [82]) и 2-разрядное поле явного уведомления о загруженности сети (Explicit Congestion Notification, ECN) (RFC 3168 [100]) заменили 8-разрядное поле тип службы (сервиса) (type-of-service, TOS), которое описывалось в RFC 1349 [5]. Все 8 разрядов этого поля можно установить с помощью параметра сокета IP_TOS (см. раздел 7.6), хотя ядро может перезаписать любое установленное нами значение при проведении политики Diffserv или реализации ECN.

■ Поле общая длина (total length) имеет размер 16 бит и задает полную длину IP- дейтаграммы в байтах, включая заголовок IPv4. Количество данных в дейтаграмме равно значению этого поля минус длина заголовка, умноженная на 4. Данное поле необходимо, поскольку некоторые каналы передачи данных заполняют кадр до некоторой минимальной длины (например, Ethernet) и возможна ситуация, когда размер действительной IP-дейтаграммы окажется меньше требуемого минимума.

1 ... 237 238 239 240 241 242 243 244 245 ... 263
Перейти на страницу:
Тут вы можете бесплатно читать книгу UNIX: разработка сетевых приложений - Уильям Стивенс.
Комментарии