UNIX: разработка сетевых приложений
Шрифт:
Заполнение структуры запроса и отправка поставщику
18-26
В TPI определена структура T_conn_req
, содержащая адрес протокола и параметры для соединения:
struct T_conn_req {
long PRIM_type; /* T_CONN_REQ */
long DEST_length; /* длина адреса получателя */
long DEST_offset; /* смещение адреса получателя */
long OPT_length; /* длина параметров */
long OPT_offset; /*
смещение параметров */
/* затем следуют адреса протокола и параметры соединения */
};
Как и в случае функции
tpi_bind
, мы определяем свою собственную структуру с именем conn_req
, которая включает в себя структуру T_conn_req
, а также содержит место для адреса протокола. Мы заполняем структуру conn_req
, обнуляя поля OPT_length
и OPT_offset
. Мы вызываем функцию putmsg
только с управляющей информацией и флагом 0 для отправки сообщения типа M_PROTO
вниз по потоку. Чтение ответа
27-45
Мы вызываем функцию getmsg
, ожидая получить в ответ либо сообщение T_OK_ACK
, если было начато установление соединения, либо сообщение T_ERROR_ACK
(которые мы уже показывали выше). В случае ошибки мы завершаем выполнение программы. Поскольку мы не знаем, сообщение какого типа мы получим, то определяем объединение с именем T_primitives
для приема всех возможных запросов и ответов и размещаем это объединение в памяти как входной буфер для управляющей информации при вызове функции getmsg
.
struct T_ok_ack {
long PRIM_type; /* T_OK_ACK */
long CORRECT_prim; /* корректный примитив */
};
Ожидание завершения установления соединения
46-65
Сообщение T_OK_ACK
, полученное нами на предыдущем этапе, указывает лишь на то, что соединение успешно начало устанавливаться. Теперь нам нужно дождаться сообщения T_CONN_CON
, указывающего на то, что другой конец соединения подтверждает получение запроса на соединение.
struct T_conn_con {
long PRIM_type; /* T_CONN_CON */
long RES_length; /* длина адреса собеседника */
long RES_offset; /* смещение адреса собеседника */
long OPT_length; /* длина параметра */
long OPT_offset; /* смещение параметра */
/* далее следуют адрес протокола и параметры собеседника */
};
Мы снова вызываем функцию
getmsg
, но ожидаемое нами сообщение посылается как сообщение типа M_PROTO
, а не как сообщение M_PCPROTO
, поэтому мы обнуляем флаги. Если мы получаем сообщение T_CONN_CON
,
значит, соединение установлено, и мы возвращаемся, но если соединение не было установлено (по причине того, что процесс собеседника не запущен, истекло время ожидания или еще по какой-либо причине), то вместо этого вверх по потоку отправляется сообщение T_DISCON_IND
:
struct T_discon_ind {
long PRIM_type; /* T_DISCON_IND */
long DISCON_reason; /* причина разрыва соединения */
long SEQ_number; /* порядковый номер */
};
Мы можем посмотреть, какие ошибки могут быть возвращены поставщиком. Сначала мы задаем IP-адрес узла, на котором не запущен сервер времени и даты:
solaris26 % tpi_daytime 192.168.1.10
tpi_connect2: T_DISCON_IND from conn (146)
Код 146 соответствует ошибке
ECONNREFUSED
. Затем мы задаем IP-адрес, который не связан с Интернетом:
solaris26 % tpi_daytime 192.3.4.5
tpi_connect2: T_DISCON_IND from conn (145)
На этот раз возвращается ошибка
ETIMEDOUT
. Но если мы снова запустим нашу программу, задавая тот же самый IP-адрес, то получим другую ошибку:
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)
Поделиться с друзьями: