UNIX: разработка сетевых приложений
Часть 362 из 399 Информация о книге
Заполнение структуры адреса сервера, установление соединенияМы заполняем другую структуру адреса сокета Интернета, внося в нее IP-адрес сервера (из командной строки) и порт (13). Мы вызываем нашу функцию17-22для установления соединения.tpi_connectСчитывание данных с сервера, копирование в стандартный поток выводаКак и в случае других клиентов времени и даты, мы просто копируем данные, пришедшие по соединению, в стандартный поток вывода, останавливаясь при получении признака конца файла, присланного сервером (например, сегмент FIN). Мы сделали этот цикл похожим на тот, который использовался в коде сокетного клиента (см. листинг 1.1), поскольку наша функция23-33при нормальном завершении соединения на стороне сервера будет возвращать нулевое значение. Затем мы вызываем нашу функциюtpi_readдля того, чтобы закрыть эту точку доступа.tpi_closeНаша функция
показана в листинге 31.3.tpi_bindЛистинг 31.3. Функция tpi_bind: связывание локального адреса с точкой доступа
//streams/tpi_bind.c1 #include "tpi_daytime.h"2 void3 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_reqstruct T_bind_req {long PRIM_type; /* T_BIND_REQ */long ADDR_length; /* длина адреса */long ADDR_offset; /* смещение адреса */unsigned long CONIND_number; /* сообщения о соединении *//* далее следует адрес протокола для связывания */};Все запросы TPI определяются как структуры, начинающиеся с поля типа
. Мы определяем свою собственную структуруlong, начинающуюся со структурыbind_req, после которой располагается буфер, содержащий локальный адрес для связывания. TPI ничего не говорит о содержимом буфера — оно определяется поставщиком. Поставщик TCP предполагает, что этот буфер содержит структуруT_bind_req.sockaddr_inМы заполняем структуру
, устанавливая элементT_bind_req_length равным размеру адреса (16 байт для структуры адреса сокета Интернета), а элементADDR— равным байтовому сдвигу адреса (он следует непосредственно за структуройADDR_offset). У нас нет гарантии, что это местоположение соответствующим образом выровнено для записи структурыT_bind_req, поэтому мы вызываем функциюsockaddr_in, чтобы скопировать структуру вызывающего процесса в нашу структуруmemcpy. Мы присваиваем элементуbind_reqнулевое значение, потому что мы находимся на стороне клиента, а не на стороне сервера.CONIND_numberВызов функции putmsgTPI требует, чтобы только что созданная нами структура была передана поставщику как одно сообщение21-23. Следовательно, мы вызываем функциюM_PROTO, задавая структуруputmsgв качестве управляющей информации, без каких-либо данных и с флагом 0.bind_req