Завершая разговор о программном интерфейсе TLI, необходимо упомянуть об обработке ошибок. Для большинства функций TLI свидетельством ошибки является получение -1 в качестве возвращаемого значения. Напротив, в случае нормального завершения эти функции возвращают 0. Как правило, при неудачном завершении функции TLI код ошибки сохраняется в переменной
t_errno
, подобно тому, как переменная
errno
хранит код ошибки системного вызова. Для вывода сообщения, расшифровывающего причину ошибки, используется функция t_error(3N):
#include <tiuser.h>
void t_error(const char *errmsg);
При
вызове t_error(3N) после неудачного завершения какой-либо функции TLI будет выведено сообщение
errmsg
, определенное разработчиком программы, за которым последует расшифровка ошибки, связанной с кодом
t_errno
. Если значение
t_errno
равно
TSYSERR
, то расшифровка представляет собой стандартное сообщение о системной ошибке, связанной с переменной
errno
.
В заключение в качестве иллюстрации программного интерфейса TLI приведем пример приложения клиент-сервер. Как и в предыдущих примерах, сервер принимает сообщения от клиента и отправляет их обратно. Клиент, в свою очередь, выводит полученное сообщение на экран. В качестве сообщения, как и прежде, выступает жизнерадостное приветствие "Здравствуй, мир!".
Сервер
#include <sys/types.h>
#include <sys/socket.h>
#include <tiuser.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <fcntl.h>
#include <netdb.h>
/* Номер порта, известный клиентам */
#define PORTNUM 1500
main(argc, argv)
int argc;
char *argv[];
{
/* Дескрипторы транспортных узлов сервера */
int tn, ntn;
int pid, flags;
int nport;
/* Адреса транспортных узлов сервера и клиента */
struct sockaddr_in serv_addr, *clnt_addr;
struct hostent *hp;
char buf[80], hname[80];
struct t_bind req;
struct t_call *call;
/* Создадим транспортный узел. В качестве поставщика
транспортных услуг выберем модуль TCP */
if ((tn = t_open("/dev/tcp", O_RDWR, NULL)) == -1) {
t_error("Ошибка вызова t_open");
exit(1);
}
/* Зададим адрес транспортного узла — он должен быть
известен клиенту */
nport = PORTNUM;
/* Приведем в соответствие порядок следования байтов для хоста
и сети */
nport = htons((u_short)nport);
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = nport;
req.addr.maxlen = sizeof(serv_addr);
req.addr.len = sizeof(serv_addr);
req.addr.buf = (char*)&serv_addr;
/* Максимальное число запросов, ожидающих обработки,
установим равным 5 */
req.qlen = 5;
/* Свяжем узел с запросом */
if (t_bind(tn, &req, (struct t_bind*)0) < 0) {
t_error("Ошибка вызова t_bind;
exit(1);
}
fprintf(stderr, "Адрес сервера: %s\n",
inet_ntoa(serv_addr.sin_addr));
/* Поскольку в структуре t_call нам понадобится только буфер
для хранения адреса клиента, разместим ее динамически */