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

ЖАНРЫ

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

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.

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