И.А. Волкова, И.Г. Головин, М.Г. Мальковский - Модельный SQL-интерпретатор (1119418), страница 2
Текст из файла (страница 2)
Функция recv.
Функция служит для чтения данных из сокета.
Прототип:
int recv(int s, char * buf, int len, int flags);
Первый аргумент - сокет-дескриптор, из которого читаются данные. Второй и третий аргументы - соответственно, адрес и длина буфера для записи читаемых данных. Четвертый параметр - это комбинация битовых флагов, управляющих режимами чтения. Если аргумент flags равен нулю, то считанные данные удаляются из сокета. Если значение flags есть MSG_PEEK, то данные не удаляются и могут быть считаны последущим вызовом ( или вызовами ) recv.
Функция возвращает число считанных байтов или -1 в случае ошибки. Следует отметить, что нулевое значение не является ошибкой. Оно сигнализирует об отсутствии записанных в сокет процессом-поставщиком данных.
Функция shutdown.
Эта функция используется для немедленного закрытия всех или части связей на сокет.
Прототип:
int shutdown(int s, int how);
Первый аргумент функции - сокет-дескриптор, который должен быть закрыт. Второй аргумент - целое значение, указывающее, каким образом закрывается сокет, а именно:
0 - сокет закрывается для чтения;
1 - сокет закрывается для записи;
2 - сокет закрывается для чтения и для записи.
Функция close.
Эта функция закрывает сокет и разрывает все соединения с этим сокетом. В отличие от функции shutdown функция close.может дожидаться окончания всех операций с сокетом, обеспечивая “нормальное”, а не аварийное закрытие соединений.
Прототип:
int close (int s);
Аргумент функции - закрываемый сокет-дескриптор.
Пример-оболочка программы “Клиент”
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#define ADDRESS “mysocket” /* адрес для связи */
void main ()
{ char c;
int i, s, len;
FILE *fp;
struct sockaddr_un sa;
/* получаем свой сокет-дескриптор: */
if ((s = socket (AF_UNIX, SOCK_STREAM, 0))<0){ perror (“client: socket”); exit (1);
}
/* создаем адрес, по которому будем связываться с сервером: */
sa.sun_family = AF_UNIX;
strcpy (sa.sun_path, ADDRESS);
/* пытаемся связаться с сервером: */
len = sizeof ( sa.sun_family) + strlen ( sa.sun_path);
if ( connect ( s, &sa, len) < 0 ){
perror (“client: connect”); exit (1);
}
/*--------------------------------------------- */
/* читаем сообщения сервера */
fp = fdopen (s, “r”);
c = fgetc (fp);
/* обрабатываем информацию от сервера
...................................
*/
/* посылаем ответ серверу */
send (s, “client”, 7, 0);
/* продолжаем диалог с сервером, пока в этом есть
необходимость
............................
*/
/* завершаем сеанс работы */
close (s);
exit (0);
}
Пример-оболочка программы “Сервер”
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#define ADDRESS “mysocket” /* адрес для связи */
void main ()
{ char c;
int i, d, d1, len, ca_len;
FILE *fp;
struct sockaddr_un sa, ca;
/* получаем свой сокет-дескриптор: */
if((d = socket (AF_UNIX, SOCK_STREAM, 0)) < 0){
perror (“client: socket”); exit (1);
}
/* создаем адрес, c которым будут связываться клиенты */
sa.sun_family = AF_UNIX;
strcpy (sa.sun_path, ADDRESS);
/* связываем адрес с сокетом;
уничтожаем файл с именем ADDRESS, если он существует,
для того, чтобы вызов bind завершился успешно
*/
unlink (ADDRESS);
len = sizeof ( sa.sun_family) + strlen (sa.sun_path);
if ( bind ( d, &sa, len) < 0 ) {
perror (“server: bind”); exit (1);
}
/* слушаем запросы на сокет */
if ( listen ( d, 5) < 0 ) {
perror (“server: listen”); exit (1);
}
/* связываемся с клиентом через неименованный сокет с дескриптором d1:
*/
if (( d1 = accept ( d, &ca, &ca_len)) < 0 ) {
perror (“server: accept”); exit (1);
}
/* ------------------------------------------ */
/* пишем клиенту: */
send (d1, “server”, 7, 0);
/* читаем запрос клиента */
fp = fdopen (d1, “r”);
c = fgetc (fp);
/* ................................ */
/* обрабатываем запрос клиента,
посылаем ответ и т.д.
...........................
*/
/* завершаем сеанс работы */
close (d1);
exit (0);
}
Средства межпроцессного взаимодействия для сети ЭВМ
(ОС Berkeley UNIX)
Сетевые средства ОС UNIX также базируются на механизме сокетов, рассмотренном выше, но работают в Internet -домене.
В UNIX домене адрес программы специфицируется стандартным UNIX path name. Этот домен не подходит для работы с сетью, так как не все компьютеры, связанные в сеть, работают в ОС UNIX, а следовательно, могут иметь другие способы спецификации адресов, поэтому при организации межпроцессного взаимодействия для сети ЭВМ используется Internet-домен.
Адреса программ, используемые в Internet-домене, состоят из двух чисел: первое - тридцатидвухбитовый номер компьютера, на котором находится программа, второе - шестнадцатибитовый номер порта.
Каждый компьютер в сети, будь то глобальная или локальная сеть, имеет уникальный (в данной сети) номер, однозначно идентифицирующий компьютер при сетевом взаимодействии. Отметим, что хотя сетевой номер похож на имя машины, но это не то же самое, что хост-имя (hostname- главное, основное имя ) компьютера. Хост-имя неудобно использовать в качестве сетевого адреса, так как, во-первых, нельзя гарантировать уникальность хост-имени в сети, во-вторых, хост-имя - как правило, строка (переменной длины) в отличие от сетевого номера, имеющего фиксированный формат и длину. Кроме того, допускается,что один и тот же компьютер может принадлежать более, чем одной сети, т.е. иметь несколько сетевых номеров. Каждый сетевой номер говорит ОС, как связаться с конкретной ЭВМ, используя различные сетевые пути.
Однако, сетевого номера недостаточно для идентификации сетевого взаимодействия, так как в ОС UNIX одновременно может выполняться несколько процессов, каждый из которых может поддерживать свои сетевые соединения. Поэтому каждый процесс, с которым можно установить сетевое соединение, имеет свой номер порта, который в некотором смысле аналогичен имени файла, используемому в UNIX-домене.
Например, сервер передачи файлов по протоколу FTP использует порт 21. Поэтому программа, желающая связаться с FTP-сервером на компьютере с сетевым номером 12345, должна указать Internet-адрес (12345, 21).
Преобразование хост-имени в сетевые номера
Пользователю обычно известно хост-имя своего компьютера. Если же это не так, то можно воспользоваться функцией gethostname.
Функция gethostname.
Прототип:
int gethostname (char * buffer, int buflen);
Первый аргумент функции - символьный буфер, в который в результате выполнения функции запишется строка - хост-имя компьютера; второй аргумент - целое число - длина символьного буфера. В случае, если длины буфера не хватает для хранения хост-имени, функция возвращает -1, иначе - 0.
Чтобы использовать хост-имя в качестве сетевого адреса, надо его преобразовать в сетевой номер. Соответствия между хост-именами и сетевыми номерами хранятся в системном текстовом файле /etc/hosts .
Функция gethostbyname.
Прототип:
struct hostent * gethostbyname (char * hostname);
У этой функции один аргумент (строка символов hostname) - хост-имя. Она возвращает указатель на структуру struct hostent , определенную во включаемом файле <netdb.h> :
struct hostent
{ char* h_name; /* hostname ЭВМ */
сhar** h_aliases; /* список синонимов */
int h_addrtype; /* тип адресов ЭВМ */
int h_length; /* длина адреса */
char** h_addr_list /* список адресов (для разных сетей) */
#define h_addr h_addr_list[0]
};
Если же указанного хост-имени нет в базе данных, возвращается константа NULL.
Получение номера порта
Большинство обслуживающих сеть программ имеют стандартные общеизвестные номера портов, которые помещаются в начале спецификаций протоколов, которые их используют. Это позволяет программе-клиенту на одной машине контактировать с программой-сервером на любой другой машине без определения того, на каком конкретно порте находится соответствующая обслуживающая программа на данной ЭВМ.
Номера портов для стандартных обслуживающих программ перечислены вместе с их именами в текстовом файле /etc/services .
Функция getservbyname.
Эта функция используется для того, чтобы получить номер порта конкретной обслуживающей программы.
Прототип:
struct servent* getservbyname(char * name, char * proto);
Первый аргумент функции - имя нужной обслуживающей программы, второй аргумент - строка символов: “tcp” или “udp”. Второй аргумент нужен для того, чтобы определить, хочет ли программа порт для типа связи SOCK_STREAM (“tcp”) или для типа связи SOCK_DGRAM (“udp”).
Функция возвращает указатель на структуру типа struct servent , определенную во включаемом файле <netdb.h> :
struct servent {
char* s_name; /* имя обслуживающей программы */
char** s_aliases; /* список синонимов */
int s_port; /* номер порта */
char* s_proto; /* используемый протокол */
};
Если же обслуживающая программа не найдена в базе данных, возвращается константа NULL.
Обычные пользовательские программы могут использовать любые свободные порты, но их номера должны лежать в пределах от 1025 до 32767 в соответствии с требованиями ОС UNIX и ARPANET (international network) administration, зарезервировавших номера меньшие 1025 для своих обслуживающих программ и специальных пользователей.
Порядок байтов в сети
Известно, что целые числа хранятся в различных компьютерах разными способами. Некоторые компьютеры хранят целые числа в перевернутом виде, т.е. старшие байты числа имеют меньший адрес по сравнению с младшим байтом, в других же наоборот старший байт имеет больший адрес. Чтобы избежать путаницы при взаимодействии машин, использующих различный порядок байтов, network software требует, чтобы все целочисленные данные были представлены в сетевом байтовом порядке. Для того, чтобы привести целые числа к сетевому байтовому порядку, используются следующие две функции.
Функция htons.
Прототип:
short htons (short i);
Функция htonl.
Прототип:
long htonl (long i);
Эти функции переводят короткие (htons) и длинные (htonl) целые числа из байтового порядка компьютера в сетевой.
Следующие две функции производят обратные действия, т.е. переводят короткие и длинные целые числа соответственно из сетевого байтового порядка в представление, принятое на данном компьютере.
Функция ntohs.
Прототип:
short ntohs (short i);
Функция ntohl.
Прототип:
long ntohl (long i);
Замечание: функции gethostbyname и getservbyname возвращают все данные и их структуры в сетевом байтовом порядке, т.е. дополнительные преобразования не требуются.
Функции работы с сетью ЭВМ
Системные функции для межпроцессного взаимодействия, используемые при решении сетевых задач, те же, что и для межпроцессного взаимодействия на одной машине. Существует лишь несколько различий в параметрах, передаваемых этим системным функциям.
Во-первых, в качестве первого параметра функции socket используется константа AF_INET, определяющая Internet-домен. В качестве второго параметра также указывается либо SOCK_STREAM либо SOCK_DGRAM.
Во вторых, в качестве типа sockaddr-структуры, используемой функциями accept, bind, connect, send и recv, употребляется тип sockadd_in, который объявлен во включаемом файле <netinet/in.h>:
struct sockaddr_in {
short sin_family;