Операционная система UNIX - Андрей Робачевский
Шрифт:
Интервал:
Закладка:
while (1) {
int addrlen;
bzero(&clnt_addr, sizeof(clnt_addr));
addrlen = sizeof(clnt_addr);
/* Примем запрос. Новый сокет ns становится
коммуникационным узлом созданного виртуального канала */
if ((ns=accept(s, (struct sockaddr*)&clnt_addr,
&addrlen))==-1) {
perror("Ошибка вызова accept()");
exit(1);
}
/* Выведем информацию о клиенте */
fprintf(stderr, "Клиент = %sn",
inet_ntoa(clnt_addr.sin_addr));
/* Создадим процесс для работы с клиентом */
if ((pid=fork())==-1) {
perror("Ошибка вызова fork()");
exit(1);
}
if (pid==0) {
int nbytes;
int fout;
/* Дочерний процесс: этот сокет нам не нужен. Он
по-прежнему используется для получения запросов */
close(s);
/* Получим сообщение от клиента и передадим его обратно */
while ((nbytes = recv(ns, buf, sizeof(buf), 0)) !=0) {
send(ns, buf, sizeof(buf), 0);
}
close(ns);
exit(0);
}
/* Родительский процесс: этот сокет нам не нужен. Он
используется дочерним процессом для обмена данными */
close(ns);
}
}
Клиент#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <fcntl.h>
#include <netdb.h>
/* Номер порта, который обслуживается сервером */
#define PORTNUM 1500
main (argc, argv)
char *argv[];
int argc;
{
int s;
int pid;
int i, j;
struct sockaddr_in serv_addr;
struct hostent *hp;
char buf[80]="Hello, World!";
/* В качестве аргумента клиенту передается доменное имя
хоста, на котором запущен сервер. Произведем трансляцию
доменного имени в адрес */
if ((hp = gethostbyname(argv[1])) == 0) {
perror("Ошибка вызова gethostbyname()");
exit(3);
}
bzero(&serv_addr, sizeof(serv_addr));
bcopy(hp->h_addr, &serv_addr.sin_addr, hp->h_length);
serv_addr.sin_family = hp->h_addrtype;
serv_addr.sin_port = htons(PORTNUM);
/* Создадим сокет */
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Ошибка вызова socket!)");
exit(1);
}
fprintf(stderr, "Адрес клиента: %sn",
inet_ntoa(serv_addr.sin_addr));
/* Создадим виртуальный канал */
if (connect (s, (struct sockaddr*)&serv_addr,
sizeof(serv_addr)) == -1) {
perror("Ошибка вызова connect()");
exit(1);
}
/* Отправим серверу сообщение и получим его обратно */
send(s, buf, sizeof(buf), 0);
if (recv(s, buf, sizeof(buf) , 0) < 0) {
perror("Ошибка вызова recv()");
exit(1);
}
/* Выведем полученное сообщение на экран */
printf("Получено от сервера: %sn", buf);
close(s);
printf("Клиент завершил работу nn");
}
Программный интерфейс TLI
При обсуждении реализации сетевой поддержки в BSD UNIX был рассмотрен программный интерфейс доступа к сетевым ресурсам, основанный на сокетах. В данном разделе описан интерфейс транспортного уровня (Transport Layer Interface, TLI), который обеспечивает взаимодействие прикладных программ с транспортными протоколами.
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 <tiuser.h>
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_uderrCo структурами, приведенными в таблице, мы познакомимся при обсуждении функций 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).