Заполнение структуры адреса сервера, установление соединения
17-22
Мы заполняем другую структуру адреса сокета Интернета, внося в нее IP-адрес сервера (из командной строки) и порт (13). Мы вызываем нашу функцию
tpi_connect
для установления соединения.
Считывание данных с сервера, копирование в стандартный поток вывода
23-33
Как и в случае других клиентов времени и даты, мы просто копируем данные, пришедшие по соединению, в стандартный поток вывода, останавливаясь при получении признака конца файла, присланного сервером (например, сегмент FIN). Мы сделали этот цикл похожим на тот, который использовался в коде сокетного клиента (см. листинг 1.1), поскольку наша функция
tpi_read
при нормальном завершении соединения на стороне сервера будет возвращать нулевое значение. Затем мы вызываем нашу функцию
tpi_close
для того, чтобы закрыть эту точку доступа.
Наша функция
tpi_bind
показана в листинге 31.3.
Листинг 31.3. Функция tpi_bind: связывание локального адреса с точкой доступа
//streams/tpi_bind.c
1 #include "tpi_daytime.h"
2 void
3 tpi_bind(int fd, const void *addr, size_t addrlen)
4 {
5 struct {
6 struct T_bind_req msg_hdr;
7 char addr[128];
8 } bind_req;
9 struct {
10 struct T_bind_ack msg_hdr;
11 char addr[128];
12 } bind_ack;
13 struct strbuf ctlbuf;
14 struct T_error_ack *error_ack;
15 int flags;
16 bind_req.msg_hdr.PRIM_type = T_BIND_REQ;
17 bind_req.msg_hdr.ADDR_length = addrlen;
18 bind_req.msg_hdr.ADDR_offset = sizeof(struct T_bind_req);
19 bind_req.msg_hdr.CONIND_number = 0;
20 memcpy(bind_req.addr, addr, addrlen); /* sockaddr_in{} */
21 ctlbuf.len = sizeof(struct T_bind_req) + addrlen;
22 ctlbuf.buf = (char*)&bind_req;
23 Putmsg(fd, &ctlbuf, NULL, 0);
24 ctlbuf.maxlen = sizeof(bind_ack);
25 ctlbuf.len = 0;
26 ctlbuf.buf = (char*)&bind_ack;
27 flags = RS_HIPRI;
28 Getmsg(fd, &ctlbuf, NULL, &flags);
29 if (ctlbuf.len < (int)sizeof(long))
30 err_quit("bad length from getmsg");
31 switch (bind_ack.msg_hdr.PRIM_type) {
32 case T_BIND_ACK:
33 return;
34 case T_ERROR_ACK:
35 if (ctlbuf.len < (int)sizeof(struct T_error_ack))
36 err_quit("bad length for T_ERROR_ACK");
37 error_ack = (struct T_error_ack*)&bind_ack.msg_hdr;
38 err_quit("T_ERROR_ACK from bind (%d, %d)",
39 error_ack->TLI_error, error_ack->UNIX_error);
40 default:
41 err_quit("unexpected message type: %d", bind_ack.msg_hdr.PRlM_type);
42 }
43 }
Заполнение структуры T_bind_req
16-20
Заголовочный файл
<sys/tihdr.h>
определяет структуру
T_bind_req
:
struct T_bind_req {
long PRIM_type; /* T_BIND_REQ */
long ADDR_length; /* длина адреса */
long ADDR_offset; /* смещение адреса */
unsigned long CONIND_number; /* сообщения о соединении */
/* далее следует адрес протокола для связывания */
};
Все запросы TPI определяются как структуры, начинающиеся с поля типа
long
. Мы определяем свою собственную структуру
bind_req
, начинающуюся со структуры
T_bind_req
, после которой располагается буфер, содержащий локальный адрес для связывания. TPI ничего не говорит о содержимом буфера — оно определяется поставщиком. Поставщик TCP предполагает, что этот буфер содержит структуру
sockaddr_in
.
Мы заполняем структуру
T_bind_req
, устанавливая элемент
ADDR
_length равным размеру адреса (16 байт для структуры адреса сокета Интернета), а элемент
ADDR_offset
— равным байтовому сдвигу адреса (он следует непосредственно за структурой
T_bind_req
). У нас нет гарантии, что это местоположение соответствующим образом выровнено для записи структуры
sockaddr_in
, поэтому мы вызываем функцию
memcpy
, чтобы скопировать структуру вызывающего процесса в нашу структуру
bind_req
. Мы присваиваем элементу
CONIND_number
нулевое значение, потому что мы находимся на стороне клиента, а не на стороне сервера.
Вызов функции putmsg
21-23
TPI требует, чтобы только что созданная нами структура была передана поставщику как одно сообщение
M_PROTO
. Следовательно, мы вызываем функцию
putmsg
, задавая структуру
bind_req
в качестве управляющей информации, без каких-либо данных и с флагом 0.