Программирование >>  Структура ядра и системные вызовы 

1 ... 59 60 61 [ 62 ] 63 64 65 ... 98


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) {



1 ... 59 60 61 [ 62 ] 63 64 65 ... 98

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика