long DISCON_reason; /* причина разрыва соединения */
long SEQ_number; /* порядковый номер */
};
Мы можем посмотреть, какие ошибки могут быть возвращены поставщиком. Сначала мы задаем IP-адрес узла, на котором не запущен сервер времени и даты:
solaris26 % <b>tpi_daytime 192.168.1.10</b>
tpi_connect2: T_DISCON_IND from conn (146)
Код 146 соответствует ошибке
ECONNREFUSED
. Затем мы задаем IP-адрес, который не связан с Интернетом:
solaris26 % <b>tpi_daytime 192.3.4.5</b>
tpi_connect2: T_DISCON_IND from conn (145)
На этот раз возвращается ошибка
ETIMEDOUT
. Но если мы снова запустим нашу программу, задавая тот же самый IP-адрес, то получим другую ошибку:
solaris26 % <b>tpi_daytime 192.3.4.5</b>
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 */