TLI был впервые представлен в UNIX System V Release 3.0 в 1986 году. Этот программный интерфейс тесно связан с сетевой подсистемой UNIX, основанной на архитектуре STREAMS, изолируя от прикладной программы особенности сетевой архитектуры. Вместо того чтобы непосредственно пользоваться общими функциями STREAMS, рассмотренными в предыдущей главе, TLI позволяет использовать специальный набор вызовов, специально предназначенных для сетевых приложений. Для преобразования вызовов TLI в функции интерфейса STREAMS используется библиотека TLI, которая в большинстве систем UNIX имеет название libnsl.aили libnsl.so.
Схема использования функций TLI во многом сходна с рассмотренным интерфейсом сокетов и зависит от типа используемого протокола — с предварительным установлением соединения (например, TCP) или без него (например, UDP).
На рис. 6.18 и 6.19 представлены схемы использования функций TLI для транспортных протоколов с предварительным установлением соединения и без установления соединения. Можно отметить, что эти схемы очень похожи на те, с которыми мы уже встречались в разделе "Межпроцессное взаимодействие в BSD UNIX. Сокеты" главы 3 при обсуждении сокетов. Некоторые различия отмечены ниже при описании функций TLI.
Рис. 6.18.Схема вызова функций TLI для протокола с предварительным установлением соединения
Рис. 6.19. Схема вызова функций TLI для протокола без предварительного установления соединения
Прежде чем перейти к обсуждению функций TLI, остановимся на определении адреса коммуникационного узла. TLI не накладывает никаких ограничений на формат адреса, возлагая интерпретацию на протоколы нижнего уровня. Благодаря этому, один и тот же интерфейс может быть использован при работе с различными семействами сетевых протоколов.
Для определения адреса TLI предоставляет общую структуру данных netbuf
, имеющую вид:
struct netbuf {
unsigned int maxlen;
unsigned int len;
char *buf;
}
Поле buf
указывает на буфер, в котором может передаваться адрес узла, maxlen
определяет его размер, a len
— количество данных в буфере, т.е. размер адреса. Эта структура по своему назначению похожа на структуру sockaddr
, которая является общим определением адреса коммуникационного узла для сокетов. Далее рассматривается пример сетевого приложения, основанного на TLI, где показано, как netbuf
используется при передаче адреса для протоколов TCP/IP.
Структура netbuf
используется в TLI для хранения не только адреса, но и другой информации — опций протокола и прикладных данных. Эта структура является составной частью более сложных структур данных, используемых при передаче параметров в функциях TLI. Для упрощения динамического размещения этих структур библиотека TLI предоставляет две функции: t_alloc(3N) для размещения структуры и t_free(3N) для освобождения памяти. Эти функции имеют следующий вид:
#include
char *t_alloc(int fd, int struct_type, int fields);
int t_free(char *ptr, int struct_type);
Аргумент struct_type
определяет, для какой структуры данных выделяется память. Он может принимать следующие значения:
Значение поля struct_type |
Структура данных |
T_BIND |
struct t_bind |
T_CALL |
struct t_call |
T_DIS |
struct t_discon |
T_INFO |
struct t_info |
T_OPTMGMT |
struct t_optmgmt |
T_UNITDATA |
struct t_unitdata |
T_UDERROR |
struct t_uderr |
Co структурами, приведенными в таблице, мы познакомимся при обсуждении функций TLI. Большинство из них включают несколько элементов netbuf
. Поскольку в некоторых случаях может отсутствовать необходимость размещения всех элементов netfuf
, поле fields
позволяет указать, какие конкретно буферы необходимо разместить для данной структуры:
Значение поля fields |
Размещаемые и инициализируемые поля |
T_ALL |
Все необходимые поля |
T_ADDR |
Поле addr в структурах t_bind , t_call , t_unitdata , t_uderr |
T_OPT |
Поле opt в структурах t_call , t_unitdata , t_uderr , t_optmgmt |
T_UDATA |
Поле udata в структурах t_call , t_unitdata , t_discon |
Отметим одну особенность. Фактический размер буфера и, соответственно, структуры netbuf
зависят от значения поля maxlen
этой структуры. В свою очередь, этот параметр зависит от конкретного поставщика транспортных услуг — именно он определяет максимальный размер адреса, опций и прикладных данных. Чуть позже мы увидим, что эта информация ассоциирована с транспортным узлом и может быть получена после его создания с помощью функции t_open(3N) . Поэтому для определения фактического размера размещаемых структур в функции t_аlloc(3N) необходим аргумент fd
, являющийся дескриптором транспортного узла, который возвращается процессу функцией t_open(3N) .
Читать дальше