Читаем Операционная система UNIX полностью

После возврата из функции t_accept(3N) между двумя узлами (connfd и удаленным узлом-клиентом) образован виртуальный канал, готовый к передаче прикладных данных.

Для обмена прикладными данными после установления соединения используются две функции: t_rcv(3N) для получения и t_snd(3N) для передачи. Они имеют следующий вид:

#include

int t_rcv(int fildes, char *buf, unsigned nbytes, int* flags);

int t_snd(int fildes, char *buf, unsigned nbytes, int flags);

Первые три аргумента соответствуют аналогичным аргументам системных вызовов read(2) и write(2). Аргумент flags функции t_snd(3N) может содержать следующие флаги:

T_EXPEDITEDУказывает на отправление экстренных данных
T_MOREУказывает, что данные составляют логическую запись, продолжение которой будет передано последующими вызовами t_snd(3N). Напомним, что TCP обеспечивает неструктурированный поток и, следовательно, не поддерживает данной возможности

Эту информацию принимающий узел получает с помощью t_rcv(3N) также через аргумент flags.

Для протоколов без предварительного установления соединения используются функции t_rcvdata(3N) и t_snddata(3N) для получения и передачи датаграмм соответственно. Функции имеют следующий вид:

#include

int t_rcvudata(int fildes, struct t_unitdata *unitdata,

 int* flags);

int t_sndudata(int fildes, struct t_unitdata *unitdata);

Для передачи данных используется структура unitdata, имеющая следующие поля:

struct netbuf addrАдрес удаленного транспортного узла
struct netbuf optОпции протокола
struct netbuf udataПрикладные данные

Созданный транспортный узел может быть закрыт с помощью функции t_close(3N). Заметим, что при этом соединение, или виртуальный канал, с которым ассоциирован данный узел, в ряде случаев не будет закрыт. Функция t_close(3N) имеет вид:

#include

int t_close(int fd);

где fd определяет транспортный узел. Вызов этой функции приведет к освобождению ресурсов, связанных с транспортным узлом, а последующий системный вызов close(2) освободит и файловый дескриптор. Судьба виртуального канала (если таковой существует) зависит от того, является ли транспортный узел, адресующий данный канал, единственным. Если это так, соединение немедленно разрывается. В противном случае, например, когда несколько файловых дескрипторов адресуют один и тот же транспортный узел, виртуальный канал продолжает существовать.

Завершая разговор о программном интерфейсе TLI, необходимо упомянуть об обработке ошибок. Для большинства функций TLI свидетельством ошибки является получение -1 в качестве возвращаемого значения. Напротив, в случае нормального завершения эти функции возвращают 0. Как правило, при неудачном завершении функции TLI код ошибки сохраняется в переменной t_errno, подобно тому, как переменная errno хранит код ошибки системного вызова. Для вывода сообщения, расшифровывающего причину ошибки, используется функция t_error(3N):

#include

void t_error(const char *errmsg);

При вызове t_error(3N) после неудачного завершения какой-либо функции TLI будет выведено сообщение errmsg, определенное разработчиком программы, за которым последует расшифровка ошибки, связанной с кодом t_errno. Если значение t_errno равно TSYSERR, то расшифровка представляет собой стандартное сообщение о системной ошибке, связанной с переменной errno.

В заключение в качестве иллюстрации программного интерфейса TLI приведем пример приложения клиент-сервер. Как и в предыдущих примерах, сервер принимает сообщения от клиента и отправляет их обратно. Клиент, в свою очередь, выводит полученное сообщение на экран. В качестве сообщения, как и прежде, выступает жизнерадостное приветствие "Здравствуй, мир!".

Сервер

#include

#include

#include

#include

#include

#include

#include

#include

/* Номер порта, известный клиентам */

#define PORTNUM 1500

main(argc, argv)

int argc;

char *argv[];

{

 /* Дескрипторы транспортных узлов сервера */

 int tn, ntn;

 int pid, flags;

 int nport;

Перейти на страницу:

Похожие книги