Модельный SQL-интерпретатор (1115016), страница 3
Текст из файла (страница 3)
*/send (d1, “server”, 7, 0);/* ................................ */putchar ( c );/* ................................ */close (d1);exit (0);}Приведенные выше примеры написаны в традиционном процедурном стиле. ЯзыкСи++ позволяет программировать как в таком стиле, так и в объектноориентированном стиле, используя мощное и выразительное понятие класса. Во второмслучае программы получаются существенно более компактными и понятными.Далее в качестве примеров приводятся оболочки программ для «Клиента» и «Сервера»,основанных на объектно-ориентированном интерфейсе для работы с сокетами (одна извозможных реализаций интерфейса приводится в приложении).2.4.
Пример-оболочка программы «Клиент» вобъектно-ориентированном стиле#include <iostream>#include "sock_wrap.h"// см. приложениеusing namespace std;using namespace ModelSQL;const char * address = "mysocket" // имя сокетаint main(int argc, char* argv[]){try {// создаём сокетUnClientSocket sock( address );// устанавливаем соединениеsock.Connect();// отправляем серверу строкуsock.PutString("Hello from client!");// печатаем на экран ответ от сервера8Модельный SQL-интерпретатор. Методическое пособие.cout << "Read from server: " << sock.GetString() << endl;// продолжаем диалог с сервером, пока в этом есть необходимость} catch (Exception & e) {// ошибка --- выводим текст сообщения на экранe.Report();}return 0;}2.5.
Пример-оболочка программы «Сервер» вобъектно-ориентированном стиле#include <iostream>#include "sock_wrap.h"// см. приложениеusing namespace std;using namespace ModelSQL;const char * address = "mysocket" // имя сокетаclass MyServerSocket : public UnServerSocket {public:MyServerSocket () : UnServerSocket (address) {}protected:void OnAccept (BaseSocket * pConn){// установлено соединение с клиентом, читаем сообщениеcout << "Read from client: " << pConn->GetString() << endl;// отправляем ответpConn->PutString("Hello from server.");// продолжаем диалог с клиентом, пока в этом есть необходимостьdelete pConn;}};int main(int argc, char* argv[]){try {// создаём серверный сокетMyServerSocket sock;// слушаем запросы на соединениеfor (;;)sock.Accept();} catch (Exception & e) {// ошибка --- выводим текст сообщения на экранe.Report();}return 0;}9Волкова И.А., Головин И.Г., Кузина Л.Н., Мальковский М.Г.3.
Средства межпроцессноговзаимодействия для сети ЭВМСетевые средства ОС UNIX также базируются на механизме сокетов, рассмотренномвыше, но работают в Internet-домене.В UNIX домене сетевой адрес процесса специфицируется стандартным описанием путик некоторому файлу. Этот домен не подходит для работы с сетью, так как не всекомпьютеры, связанные в сеть, работают в ОС UNIX, а следовательно, могут иметьразные способы спецификации адресов, поэтому при организации межпроцессноговзаимодействия для сети ЭВМ используется Internet-домен.Адресация в Internet-домене происходит следующим образом. Каждый компьютер всети имеет символическое сетевое имя (hostname) и уникальный 32-битный сетевойадрес (IP-адрес).
Для установления сетевого соединения с конкретным процессом,запущенном на некотором компьютере в сети, необходимо также указать 16-битныйномер порта, через который данный процесс готов установить сетевое соединение(пользовательским программам могут выдаваться порты с номерами от 1025 до 32767).Некоторые системные обслуживающие процессы имеют закреплённые стандартныеномера портов, такие процессы называют сетевыми сервисами. Так, например, сервиспередачи файлов по протоколу FTP использует порт 21.
Поэтому программа, желающаясвязаться с FTP-сервером на компьютере с сетевым адресом 12345, должна обратитьсяпо Internet-адресу (12345, 21).3.1. Преобразование сетевого имени в сетевойадресПользователю обычно известно сетевое имя своего компьютера. Если же это не так, томожно воспользоваться функцией gethostname.3.1.1.Функция gethostnameint gethostname (char * buffer, int buflen);Первый аргумент функции — символьный буфер, в который в результате выполненияфункции запишется строка — сетевое имя компьютера; второй аргумент — целоечисло — длина символьного буфера. В случае если длины буфера не хватает дляхранения сетевого имени, функция возвращает −1, иначе — 0.Чтобы использовать сетевое имя в качестве сетевого адреса, надо его преобразовать.Соответствия между сетевыми именами и сетевыми адресами хранятся в системномтекстовом файле /etc/hosts.3.1.2.Функция gethostbynamestruct hostent * gethostbyname (char * hostname);10Модельный SQL-интерпретатор.
Методическое пособие.У этой функции один аргумент (строка символов hostname) — сетевое имя. Онавозвращает указатель на структуру struct hostent, определенную во включаемом файле<netdb.h>:structcharсharintintcharhostent {* h_name;** h_aliases;h_addrtype;h_length;** h_addr_list//////////hostname ЭВМсписок синонимовтип адресов ЭВМдлина адресасписок адресов (для разных сетей)#define h_addr h_addr_list[0]};Если же указанного сетевого имени нет в базе данных, возвращается константа NULL.3.1.3.Получение номера портаДля всех стандартных сетевых сервисов существует перечень закреплённых за ниминомеров портов в текстовом файле /etc/services.
Это облегчает программе-клиентупроизводить соединение со стандартным сетевым сервисом на любой другой машине.3.1.4.Функция getservbynameЭта функция используется для того, чтобы получить номер порта конкретнойобслуживающей программы.struct servent* getservbyname(char * name, char * proto);Первый аргумент функции — символическое имя стандартного сервиса («telnet», «ftp»,«smtp», «pop3» и т.п.), второй аргумент — символическое имя транспортногопротокола: «tcp» или «udp».
Второй аргумент нужен для того, чтобы определить, нужноли устанавливать сессию для гарантированной доставки пакетов с сохранениемочерёдности SOCK_STREAM («tcp») или достаточно передачи коротких сообщенийSOCK_DGRAM («udp»).Функция возвращает указатель на структуру типа struct servent, определенную вовключаемом файле <netdb.h>:structcharcharintchar};servent {* s_name;** s_aliases;s_port;* s_proto;////////имя обслуживающей программысписок синонимовномер портаиспользуемый протоколЕсли имя сервиса не найдено в базе данных, возвращается константа NULL.Обычные пользовательские программы могут использовать любые свободные порты,но их номера должны лежать в пределах от 1025 до 32767 в соответствии стребованиями ОС UNIX и ARPANET (international network administration),зарезервировавших номера меньшие 1025 для стандартных и системных сервисов.11Волкова И.А., Головин И.Г., Кузина Л.Н., Мальковский М.Г.3.2.
Порядок байтов в сетиИзвестно, что целые числа хранятся в различных компьютерах по-разному. Некоторыекомпьютеры хранят целые числа в перевернутом виде, т.е. старшие байты числа имеютменьший адрес по сравнению с младшим байтом, в других же наоборот старший байтимеет больший адрес. Чтобы избежать путаницы при взаимодействии машин,использующих различный порядок байтов, network software требует, чтобы всецелочисленные данные были представлены в сетевом байтовом порядке. Для тогочтобы привести целые числа к сетевому байтовому порядку, используются следующиедве функции.3.2.1.Функция htonsshort htons (short i);3.2.2.Функция htonllong htonl (long i);Эти функции переводят короткие (htons) и длинные (htonl) целые числа из байтовогопорядка компьютера в сетевой.Следующие две функции производят обратные действия, т.е. переводят короткие идлинные целые числа соответственно из сетевого байтового порядка в представление,принятое на данном компьютере.3.2.3.Функция ntohsshort ntohs (short i);3.2.4.Функция ntohllong ntohl (long i);Замечание: функции gethostbyname и getservbyname возвращают все данные и ихструктуры в сетевом байтовом порядке, т.е.
дополнительные преобразования нетребуются.3.3. Функции работы с сетью ЭВМСистемные функции для межпроцессного взаимодействия, используемые при решениисетевых задач, те же, что и для межпроцессного взаимодействия на одной машине.Существует лишь несколько различий в параметрах, передаваемых этим системнымфункциям.12Модельный SQL-интерпретатор. Методическое пособие.Во-первых, в качестве первого параметра функции socket используется константаAF_INET, определяющая Internet-домен. В качестве второго параметра также указываетсялибо SOCK_STREAM либо SOCK_DGRAM.Во вторых, в качестве типа sockaddr-структуры, используемой функциями accept, bind,connect, send и recv, употребляется тип sockadd_in, который объявлен во включаемомфайле <netinet/in.h>:struct sockaddr_in{shortsin_family;u_shortsin_port;// номер портаstruct in_addr sin_addr;// сетевой адрес ЭВМcharsin_zero[8];};struct in_addr {u_long s_addr;};Типы u_short (unsigned short) и u_long (unsigned long) определены во включаемом файле<sys/types.h>.
Номера портов и сетевые номера компьютеров должны указываться всетевом байтовом порядке.Пример-оболочка программы «Клиент» для сети ЭВМ#include#include#include#include#include#include<sys/types.h><sys/socket.h><netinet/in.h><netdb.h><stdio.h><string.h>extern int errno;void main (){char c;ints;FILE * fp;char hostname[64];struct hostent* hp;struct sockaddr_in sin;/* в этом примере клиент и сервер выполняются на одном компьютере, но программалегко обобщается на случай разных компьютеров.
Для этого можно, например,использовать хост-имя не собственного компьютера, как ниже, а имякомпьютера, на котором выполняется процесс-сервер */// прежде всего получаем hostname собственной ЭВМ:gethostname (hostname, sizeof (hostname));// затем определяем сетевой номер своей машины:if ((hp = gethostbyname (hostname)) == NULL) {fprintf (stderr, “%s: unknown host.\n”, hostname);exit (1);}13Волкова И.А., Головин И.Г., Кузина Л.Н., Мальковский М.Г.// получаем свой сокет-дескриптор:if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {perror (“client: socket”);exit (1);}// создаем адрес, по которому будем связываться с сервером:sin.sin_family = AF_INET;sin.sin_port = htons (1234);// копируем сетевой номер:memcpy(&sin.sin_addr, hp -> h_addr, hp->h_length);// пытаемся связаться с сервером:if ( connect ( s, (struct sockaddr *)&sin, sizeof (sin)) < 0 ) {perror (“client: connect”);exit (1);}/* ------------------------------------------- */// читаем сообщения сервера, пишем серверу:fp = fdopen (s, “r”);c = fgetc (fp);/* ................................