|
Программирование >> Структура ядра и системные вызовы
int accept ( char* name, int* port p) { if (iname) return ::accept(sid, 0, 0); if (!port p II *port p == -1) ( гнездо домена UNIX struct sockaddr addr; int size = sizeof (addr) if ( (rc = ::accept(sid, &addr, fisize)) >-l) strncpy(name,addr.sadata,size), name[size]=\0; ) else ( struct sockaddr in addr; int size = sizeof (addr); if ((rc = ::accept( sid, (struct sockaddr*)Saddr, ssize)) >-l) if (name) strcpy(name,ip2name(addr.sin addr)); if (port p) *portj) = ntohs(addr.sin port); return rc; int connect( const char* hostnm, int port=-l ) /* соединение с гнездом UNIX */ if (port==-l) ( /* гнездо домена UNIX */ struct sockaddr addr; int len = constr name( addr, hostnm); if ((rc= ::connect(sid,saddr,len))<0) perror( bind ); ) else { struct sockaddr in addr; int len = constr name( addr, hostnm, port); if ((rc= ::connect(sid,(struct sockaddr *)saddr,len)) <0) perror( bind ); return rc; int write( const char* buf, int len, int flag=0, int nsid=-l ) /* передать сообщение */ t .. return ::send(nsid==-l ? sid : nsid, buf, len, flag ); int read( char* buf, int len, int flag=0, int nsid=-l ) /* прочитать сообщение */ return ::recv(nsid==-l ? sid : nsid, buf, len, flag ); /* запись в гнездо домена UNIX с указанным именем */ int writeto( const char* buf, int len, int flag, const char* name, const int port, int nsid=-l ) if (port-=-l) ( struct sockaddr addr; int size = constr name( addr, name); return ::sendto(nsid==-l ? sid : nsid, buf, len, flag, saddr, size ); } else ( struct sockaddr in addr; char buf1[80]; if (iname) ( /* использовать имя локального хоста, если не указано другое */ if (sysinfo(SI HOSTNAME,bufl,sizeof bufl)==-lL) perror( sysinfо ); name = bufl; int size = constr name( addr, name, port); return ::sendto(nsid==-l ? sid : nsid, buf, len, flag, (struct sockaddr*)saddr, size ); /* получить сообщение из гнезда домена UNIX */ int readfrom( char* buf, int len, int flag, char* name, int *port p, int nsid =-1) if (!port p II *port p == -1) ( гнездо домена UNIX struct sockaddr addr; int size = sizeof(addr); if ({rc=::recvfrom(nsid==-l ? sid : nsid, buf, len, flag, saddr, ssize)) >-l && name) strncpy(name,addr.sa data,rc), name[rc]=\0; ) else ( struct sockaddr in addr; int size = sizeof (addr); if ((rc = ::recvfrom(nsid=-l ? sid : nsid, buf, len, flag, (struct sockaddr*)saddr, ssize)) >-l) if (name) strcpy(name,ip2name(addr.sin addr)); if (port p) *port p = ntohs(addr.sin port); } return rc; #endif int shutdown( int mode = 2 ) ( return ::shutdown (sid,mode}; /* класс sock */ закрыть гнездо APT ocA: позволяет скрыть от прикладных программ низкоуровневый API гнезд. Приложение, которому необходимо открыть гнездо домена UNIX, должно указать функции-члену bind или connect лишь путевое UNIX-имя. с другой стороны, если приложению необходимо открыть гнездо домена Internet, оно должно указать только хост-имя и номер порта. Манипулировать какими-либо объектами типа struct sockaddr приложению не придется. Это экономит время программиста и уменьшает число ошибок, могущих возникнуть при создании адресов гнезд. Функции-члены класса sock почти в точности соответствуют API гнезд. Это облегчает задачу тем пользователям, которым нравится переключаться с API гнезд на объекты класса sock и наоборот. В функциях sockr.read, sock::write, sockr.readfrom и sock::writeto используется аргумент nsid, значение которому присваивается в том случае, если вызывающий процесс является сервером. Такой процесс может взаимодействовать с клиентским процессом через дескриптор гнезда nsid, полученный в результате вызова функции sock::accept. Ниже показана серверная программа, в которой класс sock используется для создания потокового гнезда, устанавливающего соединение с гнездом клиентской программы: tinclude sock.h const char* MSG2 = Hello MSG2 ; const char* MSG4 = Hello MSG4 ; main( int argc, char* argv[]) ( char buf[60], socknm[80]; int port=-l, nsid, rc; if (argc < 2) { cerr usage: argv[0] <socknameport> [<host>]\n ; return 1; /* проверить, указан ли номер порта гнезда */ (void) sscanf (argvd], %d . Sport); /* создать потоковое гнездо */ sock sp( port!=-l ? AF INET : AF UNIX, SOCK STREAM ); if (Isp.goodO) return 1; /* присвоить имя серверному гнезду */ if (sp.bind(port==-l ? argv[l] : argv(2],port) < 0) return 2; /* принять запрос на соединение от клиентского гнезда */ if ((nsid = sp.accept(О, 0)) <О) return 1; /* прочитать сообщение MSG1 из клиентского гнезда */ if ((rc=sp.read(buf, sizeof buf, 0, nsid)) < 0) return 5; cerr server: receive msg: buf \n ; /* записать сообщение MSG2 в клиентское гнездо */ if (sp.write(MSG2,strlen(MSG2)+1,0, nsid)<0) return 6; /* прочитать сообщение MSG3 из клиентского гнезда */ if (sp.readfrom( buf, sizeof buf, 0, socknm, sport, nsid) > 0) cerr server: recvfrom socknm msg: buf endl; /* записать сообщение MSG4 в клиентское гнездо */ if (write(nsid,MSG4,strlen(MSG4)+1)==-1) return 7; Аргументом командной строки для этой серверной программы может служить путевое UNIX-имя, на основании которого формируется имя гнезда для домена типа UNIX. Им может быть также номер порта и хост-имя (необязательно), используемые при создании гнезда для домена типа Internet. Если в последнем случае хост-имя не указано, то используется хост-имя локальной машины. Отметим, что в ситуации, когда с помощью sock::bind создается гнездо домена Internet, названная функция выводит значение номера порта в стандартный поток ошибок. Это делается для того, чтобы клиентский процесс мог обращаться к порту с этим номером при создании гнезда, предназначенного для взаимодействия с гнездом сервера. После того как гнездо создано и получило имя, серверный процесс ожидает установления клиентского соединения через это гнездо. Затем с помощью функции socL.readон принимает сообщение MSG1 из клиентского гнезда, выводит это сообщение на экран и посредством функции sockr.write передает в клиентский процесс сообщение MSG2. Сервер читает сообщение MSG2, поступившее из клиентского гнезда, с помощью функции sockr.readfrom и отвечает клиенту сообщением MSG4, передаваемым с использованием API write. Наконец, серверный процесс завершается и гнездо уничтожается посредством функции-деструктора sockr. ~sock. Ниже приведен текст программы-клиента, которая взаимодействует с указанным сервером: ♦include sock.h const char* MSGl = Hello MSGl ; const char* MSGS = Hello MSGS; niain( int argc, char* argv(]) { if (argc < 2) { cerr usage: argv(0] <socknameport> [<host>]\n ; return 1; int port=-l, rc; проверить, указан ли номер порта гнезда */ (void)sscanf(argv[l], %d ,sport); /* host может быть именем гнезда или хост-именем */ charbuf[80], *host= (port==-l) ? argv[l] : argv[2], socknm[80]; /* создать клиентское гнездо */ ii . sock sp( port!=-l ? AF INET : AF UNIX, SOCK STREAM = if (Isp.goodO) return 1; : /* соединиться с серверным гнездом */ if (sp.connect(host,port) < 0) return 8; /* передать сообщение MSGl на сервер */ if (sp.write(MSGl, strlen(MSGl)+1) < 0) return 9; /* прочитать сообщение MSG2 с сервера */ if (sp.read(buf,sizeof buf) < 0) return 10; cerr client: recv buf \n ; /* передать сообщение MSG3 на сервер */ if ((rc=sp.writeto( MSGS, strlen(MSG3)+1, 0, host, port, -1)) < 0) return 11; /* прочитать сообщение MSG4 с сервера */ if ((rc=read(sp.fd(),buf,sizeof buf))==-l) return 12; cerr client: read msg: buf endl; /* закрыть гнездо */ sp.shutdown О; Аргументы командной строки для этой программы - те же, что и в программе-сервере: путевое UNIX-имя или имя порта и необязательное хост-имя. Пользуясь этими аргументами, программа создает гнездо домена UNIX или гнездо домена Internet. Для соединения с серверным гнездом клиентская программа вызывает функцию sock::comect. С помощью функции sock::write она посылает серверу сообщение MSG1, а затем читает сообщение MSG2 (посредством вызова функции sockr.read). После этого клиент посылает на сервер с помощью функции sock::wnteto сообщение MSG3 и читает ответное сообщение MSG4, пользуясь для этого API read. Когда клиентский процесс завершается, он удаляет гнездо посредством вызова функции sock::shutdown(). Эта система типа клиент/сервер может работать с гнездами домена UNIX или с гнездами домена Internet. Ниже приведен пример взаимодействия между серверным и клиентским процессами, осуществляемого при помощи гнезд домена UNIX. Имя серверного гнезда произвольно устанавливается как SOCK: % GC -о sock stream srv sock stream srv.C -Isocket -Insl % CC -о sock stream cls sock stream cls.С -Isocket -Insl % sock stream srv SOCK & [1] 373 % sock stream cls SOCK server: receive msg: Hello MSGl client: recv Hello MSG2 server: recvfrom msg: Hello MSG3 client: read msg: Hello MSG4 [1] + Done sock stream srv SOCK Отметим, что когда сервер посредством вызова функции sockr.readfrom получает от клиента сообщение MSGS, в переменную socknm заносится значение NULL. Клиентскому гнезду имя не присваивается. В конфигурации клиент/сервер, как правило, имя присваивается только серверному гнезду. Это позволяет клиентским гнездам устанавливать с ним соединения, но не наоборот. Приведенный ниже пример показывает, как осуществляется взаимодействие между серверным и клиентским процессами при помощи гнезд домена Internet. Здесь имя машины, на которой работают оба процесса, - fruit. Поскольку имя машины указано, система может сама выбрать свободные номера портов для клиентского и серверного гнезд: % sock stream srv О fruit & [1] 374 Socket port: 32804 % sock stream cls 32804 fruit server: receive msg: Hello MSGl client: recv Hello MSG2 server: recvfrom fruit msg: Hello MSG3 client: read msg: Hello MSG4 [1] + Done sock stream srv 0 fruit Отметим, что здесь выполняются те же самые программы типа клиент/сервер, но с разными аргументами комавдной строки. Процессы, взаимодействующие по схеме клиент/сервер, выполняются с использованием гнезд домена Internet. Результаты выполнения этих программ такие же, как и в случае с гнездами домена UNIX. Более того, хотя в приведенном выше примере серверный и клиентский процессы выполняются на одной машине, при работе на разных машинах результат был бы точно таким же. Разница заключается лишь в том, что серверная программа выполняется на одной машине (например, fruit); а клиентская программа выполняется на удаленной машине с указанием номера порта серверного гнезда и хост-имени как аргументов командной строки. Сообщения, посылаемые сервером (т.е. сообщения MSG1 и MSG3) отображаются на его хост-машине, а сообщения, посьшаемые клиентами (т.е. MSG2 и MSG4) - на их удаленной хост-машине. В программах с дейтаграммными гнездами тоже используется заголовок sock.h. Первая программа, sockdatagramsrv.C, выглядит так: ♦include sock.h const char* MSG2 = Hello MSG2 ; const char* MSG4 = Hello MSG4 ; main( int argc, char* argvH) char buf[80], socknm[80]; if (argc < 2) {
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |