
2.3 Приклад використання сокета
Взаємодію поміж процесами за допомогою сокетів можна продемо-нструвати на прикладі домена UNIX. Функціональність розподіленої сис-теми у цьому разі полягає в тому, що клієнт надсилає серверові повідом-лення, сервер переспрямовує його клієнтові, який і виводить його післяотримання на термінал, оскільки у домені UNIXсокети дейтаграм практи-чно не відрізняються від сокетів потоку. Як адресу сервера можна зазначи-ти ім’я файла ./echo.server й припустити, що клієнти знають цю адресу.Сервер зв’язує створений сокет з цією локальною адресою і в такий спосібреєструється в системі. З цього моменту він готовий до приймання таопрацьовування повідомлень. Сервер починає нескінченний цикл, очікую-чи на повідомлення від клієнтів та блокуючись на виклику recvfrom(2).При отриманні повідомлення сервер надсилає його клієнтові, за допомо-гою виклику sendto(2). У додатку наведено програми серверної та клієнт-ської сторін. Клієнт створює сокет дейтаграм та зв’язує його зі своєю уні-кальною адресою, що зумовлюється унікальністю імені файла. Оскількиводночас можуть працювати кілька клієнтів, виникає проблема отриманняунікального імені. Для її розв’язання використовується функціяmktemp(3c), яка дозволяє за заданим шаблоном /tmp/clnt.XXXX та на під-ставі ідентифікатора поточного процесу отримати унікальне ім’я на замінусимволів ХХХХ. Зв’язування сокета дозволяє при відправлянні неявно за-значати його “адресу відправника”, тому сервер може повернути повідом-лення. У додатку Б (Лістинг1 та Лістинг 2) наведено вихідні тексти про-грам, що вони зреалізовують взаємодію поміж процесами, яка грунтуєтьсяна сокетах дейтаграм у домені UNIX.
3 Контрольні запитання
-
1
Які комунікаційні характеристики забезпечують сокети?
2
Які основні типи сокетів зреалізовано в BSD UNIX?
Створення програмного забезпечення телекомунікацій для роботи в ОСUNIX23
3Який системний виклик створює сокет та який він має опис?
4Що таке комунікаційні домени та які домени Ви знаєте?
5Який зв’язок існує поміж комунікаційними доменами та підтри-муваними ними сокетами?
6Якими параметрами схарактеризовується кожний комунікаційнийканал?
7Які системні виклики використовуються на серверному та клієнт-ському боці при створенні віртуального каналу з попереднім встановлен-ням з’єднання?
8Які системні виклики використовуються на серверному та клієнт-ському боці за взаємодії, яка грунтується на дейтаграмах?
9Яку довжину і формати мають адреси сокетів у різнихдоменах?
10 Яку адресу слід обирати для сервера й чому?
4 Домашнє завдання
1Коротко відповісти на контрольні запитання у письмовому вигля-ді.
2Переписати тексти клієнтської та серверної частин програми, яказорганізовує взаємодію процесів за протоколом UDP, та коментарі до них;тексти програм наведено у додатку Б (Лістинг1 та Лістинг2).
Список рекомендованої літератури
1Робачевский А. М. Операционная система UNIX.– СПб.: БХВ-Петербург, 2002.
2Ивановский С. Операционная система UNIX.– М.: Познаватель-ная книга плюс, 2000.
3Дегтярев Е. К. Введение в UNIX.– М.: МП "Память", 1991.
4http://www.freebsd.org.ru
5http://www.anriintern.com/computer/freebsd/
6http://www.linuxrsp.ru/freebsd/
|
Додаток А Тексти програм serverfifo та clientfifo |
|
|
|
Лістинг 1 |
serverfifo: |
|
|
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO "/fifo.1"
#define MAXBUF 80
main()
{
int readfd, n;
char buff[MAXBUF];
if(mkfifo(FIFO, 0777) < 0){
printf("Nemozhlyvo stvoryty FIFO\n");
exit(1);
}
if((readfd = open(FIFO, O_RDONLY)) < 0){
printf("Nemozhlyvo vidkryty FIFO\n");
exit(1);
}
while((n = read(readfd, buff, MAXBUF)) > 0)
if(write(1, buff, n) != n){
printf("Pomylka vyvodu\n");
exit(1);
}
close(readfd);
exit(0);
}
|
Лістинг 2 |
clientfifo: |
|
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO "/fifo.1"
main()
{
int writefd,n;
if((writefd = open(FIFO, O_WRONLY))<0) {
printf("Nemozhlyvo vidkryty FIFO\n");
exit(1);
}
if(write(writefd,"Dobryj denj, Svite!\n",21)!=21){
printf("Pomylka zapysu\n");
exit(1);
}
close(writefd);
if(unlink(FIFO) <0){
printf("Nemozhlyvo vyluchyty FIFO\n");
exit(1);
}
exit(0);
}
ДОДАТОК Б
Тексти програм socketserver та socketclient
|
Лістинг 1 |
socketserver: |
|
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define MAXBUF 256
#define FILE "echo.serv"
char buf[MAXBUF];
main()
{
struct sockaddr_un serv_addr, clnt_addr;
int sockfd;
int saddrlen, caddrlen, max_caddrlen, n;
if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){
printf("Nemozhlyvo stvoryty socket\n");
exit(1);
}
unlink(FILE);
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path, FILE);
saddrlen=sizeof(serv_addr.sun_family)+strlen(serv_addr.sun_path); if(bind(sockfd, (struct sockaddr*)&serv_addr, saddrlen) < 0){ printf("Pomylka svyazuvannya socketa z adresoyu\n");
exit(1);
}
max_caddrlen = sizeof(clnt_addr);
for(;;){
caddrlen = max_caddrlen;
n = recvfrom(sockfd, buf, MAXBUF, 0, (struct
sockaddr*)&clnt_addr, &caddrlen);
if(n < 0){
printf("Pomylka pryjmannya\n");
exit(1);
}
if(sendto(sockfd, buf, n, 0, (struct sockaddr*)&clnt_addr,caddrlen) !=n){
printf("Pomylka peredavannya\n");
exit(1);
}
}
}
|
Лістинг 2 |
socketclient: |
|
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#define MAXBUF 256
#define FILE "echo.serv"
#define NULL 0
char *msg = "Hello World!";
char buf[MAXBUF];
main()
{
struct sockaddr_un serv_addr, clnt_addr;
int sockfd;
int saddrlen, caddrlen, msglen, n;
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path, FILE);
saddrlen=sizeof(serv_addr.sun_family)+strlen(serv_addr.sun_path); if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){
printf("Nemozhlyvo stvoryty' socket\n");
exit(1);
}
bzero(&clnt_addr, sizeof(clnt_addr));
clnt_addr.sun_family = AF_UNIX;
strcpy(clnt_addr.sun_path, FILE);
mkstemp(clnt_addr.sun_path);
caddrlen=sizeof(clnt_addr.sun_family)+strlen(clnt_addr.sun_path); if(bind(sockfd, (struct sockaddr*)&clnt_addr, caddrlen) < 0){ printf("Pomylka svyazuvannya socketa\n");
exit(1);
}
msglen = strlen(msg);
if(sendto(sockfd, msg, msglen, 0, (struct sockaddr*)&serv_addr,saddrlen) != msglen){
printf("Pomylka peredavannya povidomlennya\n");
exit(1);
}
if((n = recvfrom(sockfd, buf, MAXBUF, 0, NULL, 0)) < 0){
printf("Pomylka otrymannya povidomlennya\n");
exit(1);
}
printf("Echo: %s\n", buf);
close(sockfd);
unlink(clnt_addr.sun_path);
exit(0);
}
ДОДАТОК В
Тексти програм servertcp та clienttcp
|
Лістинг 1 |
servertcp: |
|
#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>
#include <errno.h>
/*Номер порту сервера, відомий клієнтам*/
#define PORTNUM 1500
int main(int argc, char* argv[])
{
int s, ns, pid, nport;
struct sockaddr_in serv_addr, clnt_addr;
struct hostent *hp;
char buf[80], hname[80];
/*Перетворімо порядок слідування байтів на мережний формат*/ nport = PORTNUM;
nport = htons((u_short)nport);
/*Створімо сокет, який використовує протокол ТСР*/
if((s = socket(AF_INET, SOCK_STREAM, 0)) ==-1){
perror("Pomylka vyklyku socket()");
exit(1);
}
/*Задамо адресу комунікаційного вузла*/
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = nport;
/*Зв'яжемо сокет з цією адресою*/
if(bind(s, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) ==-1){
perror("Pomylka vyklyku bind()");
exit(1);
}
/*Передамо повідомлення із зазначенням адреси сервера*/ fprintf(stderr, "Server gotov/j: %s\n",
inet_ntoa(serv_addr.sin_addr));
/*Сервер готовий приймати запити на встановлення з'єднання. Ма-ксимальна кількість запитів, які очікують на опрацювання- 5. Якправило,цієїкількості вистачає, щоби встигнути виконати ас-серt(2) та породитидочірній процес*/
if(listen(s, 5) ==-1){
perror("Pomylka vyklyku listen()");
exit(1);
}
/*Нескінченний цикл отримування запитів та їхнього опрацьову-вання*/
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("Pomylka vyklyku accept()");
exit(1);
}
/*Виведемо інформацію про клієнта*/
fprintf(stderr, "Client = %s\n",
inet_ntoa(clnt_addr.sin_addr));
if((pid = fork()) ==-1){
perror("Pomylka vyklyku fork()");
exit(1);
}
if(pid == 0){
int nbytes, fout;
/*Дочірній процес: цей сокет нам не потрібен. Він
використовується, як і раніше, для отримання запитів*/
close(s);
/*Отримаємо повідомлення від клієнта й повернемо йо-го назад*/
while((nbytes = recv(ns, buf, sizeof(buf), 0)) != 0){ send(ns, buf, sizeof(buf), 0);
}
close(ns);
exit(0);
}
/*Батьківський процес: цей сокет нам не потрібен. Він ви-користовується дочірнім процесом для обміну даними*/
close(ns);
}
}
Лістинг 2
clienttcp:
#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>
#include <errno.h>
/*Номер порту, який обслуговується сервером*/
#define PORTNUM 1500
int |
main(int argc, char* argv[]) |
{ |
|
|
int s, pid, i, j; |
|
struct sockaddr_in serv_addr; |
|
struct hostent *hp; |
|
char buf[80] = "Hello, World!\n"; |
/*Клієнтові як аргумент передається доменне ім'я хоста, на яко-му запущено сервер. Доменне ім'я транслюється у адресу*/
if((hp = gethostbyname(argv[1])) == 0)
{
perror("Pomylka vyklyku 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("Pomylka vyklyku socket()");
exit(1);
}
fprintf(stderr, "Addresa clienta: %s\n",
inet_ntoa(serv_addr.sin_addr));
/*Створімо віртуальний канал*/
if(connect(s, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==
-1){
perror("Pomylka vyklyku connect()");
exit(1);
}
/*Передамо серверові повідомлення й отримаємо його назад*/ send(s, buf, sizeof(buf), 0);
if(recv(s, buf, sizeof(buf), 0) < 0){
perror("Pomylka vyklyku recv()");
exit(1);
}
/*Виведемо отримане повідомлення на екран*/
printf("Otrymano vid servera: %s\n", buf);
close(s);
printf("Client zavershyv robotu\n\n");
}
ДОДАТОК Г
Тексти програмsimpletcpserv та simpletcpclient
|
Лістинг 1 |
simpletcpserv: |
|
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
int main( void )
{
struct sockaddr_in local;
int s;
int s1;
int rc;
char buf[ 1 ];
local.sin_family = AF_INET;
local.sin_port = htons( 7500 );
local.sin_addr.s_addr = htonl( INADDR_ANY );
s = socket( AF_INET, SOCK_STREAM, 0 );
if ( s < 0 )
{
perror( "socket call failed" );
exit( 1 );
}
rc = bind( s, ( struct sockaddr * )&local, sizeof( local ) );
if ( rc < 0 )
{
perror( "bind call failed" );
exit( 1 );
}
rc = listen( s, 5 );
if ( rc )
{
perror( "listen call failed" );
exit( 1 );
}
s1 = accept( s, NULL, NULL );
if ( s1 < 0 )
{
perror( "accept call failed" );
exit( 1 );
}
rc = recv( s1, buf, 1, 0 );
if ( rc <= 0 )
{
perror( "recv call failed" );
exit( 1 );
}
printf( "%c\n", buf[ 0 ] );
rc = send( s1, "2", 1, 0 );
if ( rc <= 0 )
perror( "send call failed" );
exit( 0 );
}
|
|
simpletcpclient:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
int main( void )
{
struct sockaddr_in peer;
int s;
int rc;
char buf[ 1 ];
peer.sin_family = AF_INET;
peer.sin_port = htons( 7500 );
peer.sin_addr.s_addr = inet_addr( "127.0.0.1");
s = socket( AF_INET, SOCK_STREAM, 0 );
if ( s < 0 )
{
perror( "socket call failed" );
exit( 1 );
}
rc = connect( s, ( struct sockaddr * )&peer, sizeof( peer ) );
if ( rc )
{
perror( "connect call failed" );
exit( 1 );
}
rc = send( s, "1", 1, 0 );
if ( rc <= 0 )
{
perror( "send call failed" );
exit( 1 );
}
rc = recv( s, buf, 1, 0 );
if ( rc <= 0 )
perror( "recv call failed" );
else
printf( "%c\n", buf[ 0 ] );
exit( 0 );
}