Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lab_UNIX_15_19_окт2004.doc
Скачиваний:
3
Добавлен:
27.04.2019
Размер:
478.72 Кб
Скачать

2.2 Програмний інтерфейс сокетів

Сокет — це комунікаційний інтерфейс взаємодіючих процесів. Конкретний характер взаємодії залежить від типу використовуваних сокетів, а комунікаційний домен, у межах якого створено цей сокет, визначає базові властивості цієї взаємодії. Нижче наведені типи сокетів та їхні назви:

  • Сокет дейтаграм — SOCK_DGRAM

  • Сокет потоку — SOCK_STREAM

  • Сокет пакетів — SOCK_SEQPACKET

  • Сокет низького рівня — SOCK_RAW

Останній тип сокетів використовується для переглядання ICMP-повідомлень.

Для створення сокета процес має зазначити тип сокета та комунікаційний домен, у рамцях якого використовуватиметься сокет. Комунікаційний домен може підтримувати використання кількох протоколів, тому процес може зазначити конкретний комунікаційний протокол для взаємодії. Але якщо його не зазначено, система сама обирає найбільш придатний зі списку протоколів, доступних для даного комунікаційного домена. Для створення сокета використовується системний виклик socket(2):

#include <sys/types.h>

#include <sys/socket.h>

int socket (int domain, int type, int protocol)

Аргумент domain визначає комунікаційний домен, type — тип сокета, а protocol — використовуваний протокол, за умовчанням зазначається непрямо і може дорівнювати 0. У разі успішного виконання системний виклик повертає додатне ціле число, аналогічне файловому дескриптору, яке трактується далі як адреса даного сокета у подальших викликах.

Комунікаційний домен визначає сім’ю протоколів (Protocol Family), припустимих у межах даного домена. Можливі значення аргументу domain такі:

AF_UNIX — домен локальної міжпроцесної взаємодії у межах однієї ОС UNIX, внутрішні протоколи.

AF_INET — домен взаємодії процесів віддалених систем, протокол Internet (TCP/IP).

AF_NS — домен взаємодії процесів віддалених систем, протокол

Хerox NS.

Префікс AF визначає адресний простір взаємодії; припустимі є також назви з префіксом PF (Protocol Family); PF_UNIX, PF_INET тощо. Для домена AF_INET можливі такі комбінації типу сокета та використовуваного комунікаційного протоколу:

SOCK_STREAM IPPROTO_TCP (TCP)

SOCK_DGRAM IPPROTO_UDP (UDP)

SOCK_RAW IPPROTO_ICMP (ICMP)

SOCK_ RAW IPPROTO_RAW (IP)

Для однозначної ідентифікації сокета його слід прив’язати до простору імен конкретного комунікаційного домена. Кожний комунікаційний канал визначається двома вузлами — джерелом та отримувачем даних і схарактеризовується п’ятьма параметрами:

  • комунікаційним протоколом,

  • локальною адресою,

  • локальним процесом,

  • віддаленою адресою,

  • віддаленим процесом.

Адреса визначає операційну систему (чи хост мережі), а процес — конкретний додаток, який передає чи то отримує дані. Конкретні ж значення й формат цих параметрів визначаються комунікаційним доменом. При створенні сокета вказується лише один параметр — комунікаційний протокол, тому, перш ніж передавати або приймати дані, треба зазначити ще чотири параметри для комунікаційного каналу. Взаємодіючі процеси мають робити це узгоджено, використовуючи заздалегідь визначені адреси, або домовляючись щодо них у перебігу встановлення зв’язку. Процедура встановлювання цих параметрів істотно залежить від типу створюваного каналу, який визначається типом сокета та комунікаційного протоколу.

На рис. 2.1 подано взаємодію поміж процесами при віртуальному комунікаційному каналі з попереднім встановленням зв’язку.

Рисунок 2.1 — Взаємодія поміж процесами при створюванні віртуального каналу з попереднім встановленням зв’язку

На рис. 2.2 подано взаємодію, базовану на дейтаграмах без попереднього встановлення зв’язку.

Рисунок 2.2 — Взаємодія поміж процесами, базована на дейтаграмах

без попереднього встановлення зв’язку

Фактичному передаванню даних передує первісна фаза зв’язування (Binding) сокета засобами встановлення додаткової інформації, необхідної для визначення комунікаційного вузла. Зв’язування зреалізовується за допомогою системного виклику bind(2):

#include <sys/types.h>

#include <sys/socket.h>

int bind (int sockfd, struct sockaddr *localaddr, int addrlen).

Дескриптор сокета, sockfd, отримується при створюванні сокета; аргумент localaddr визначає локальну адресу, з якою треба зв’язати сокет; параметр addrlen визначає розмір адреси. У процедурі зазначається локальна адреса, яка визначає два параметри комунікаційного каналу: локальну адресу та локальний процес.

Адреса сокета залежить від комунікаційного домена, в межах якого його визначено. У титульному файлі <sys/types.h> адреса визначається у такий спосіб:

struckt sockaddr {

u_short sa_family;

char sa_data[14];

};

Поле sa_family визначає комунікаційний домен (сім’ю протоколів), а sa_data вміщує саме адресу, формат якої визначено для кожного домена. Наприклад, для внутрішнього домена UNIX адреса, визначена y <sys/un.h>, має вигляд

struckt sockaddr_un {

short sun_family; /*= = AF_UNIX*/;

char sun_path[108];

};

У цьому разі взаємодіючі процеси виконуються під керуванням однієї ОС на одному хості, комунікаційний вузол може бути однозначно визначено лише одним параметром — локальним процесом. У домені UNIX за адресу вважається ім’я файла.

Рисунок 2.3 — Адреси сокетів

У разі мережного обміну даними слід зазначити адресу як локального процесу, так і хоста, на якому виконується даний процес. Для домена Internet (сім’я протоколів ТСР/ІР) використовується формат адрес, визначений у файлі <netinet/in.h>:

struct sockaddr_in {

short sin_family; /*= = AF_INET */;

u_short sin_port;

struckt in_addr sin_addr;

char sin_zero[8];

};

Адреса цього домена (ІР-адреса) — це 32-розрядне ціле число sin_addr, а процес-додаток адресується 16-розрядним номером порту sin_port (рис. 2.3).

Зв’язування потрібне для привласнення сокетові локальної адреси й тим самим визначення комунікаційного вузла і зреалізовується системною функцією bind(2):

  1. Сервер реєструє свою адресу, яка має бути відома заздалегідь клієнтам, які “спілкуються” з сервером. Зв’язування необхідне перш ніж сервер буде готовий до приймання запитів від клієнтів.

  2. За взаємодії без попереднього встановлення зв’язку та створення віртуального каналу клієнт також повинен заздалегідь зареєструвати свою адресу, яка є унікальною в межах комунікаційного домена. У разі домена UNIX цим опікується додаток. Ця адреса не повинна бути відома серверові, тому що запит завжди ініціює клієнт і автоматично передає разом з ним власну адресу. Отримана адреса віддаленого вузла використовується сервером для мультиплексування повідомлень, які надсилаються різним клієнтам.

  3. У разі взаємодії з використанням віртуального каналу клієнт може зареєструвати власну адресу й не спрямовувати цю функцію системі.

Призначення адреси для клієнта можна виконувати за допомогою системного виклику connect(2), який встановлює зв’язок з сервером і автоматично зв’язує сокет клієнта з локальним комунікаційним вузлом. Виклик connect(2) має вигляд

#include <sys/types.h>

#include <sys/socket.h>

int connect (int sockfd, struct sockaddr *servaddr, int addrlen);

Системний виклик connect(2) створює віртуальний канал та виконується для попереднього встановлення зв’язку між комунікаційними вузлами. Клієнтові не треба зв’язувати сокет за допомогою системного виклику bind(2). Локальний вузол комунікаційного каналу зазначається дескриптором сокета sockfd, для якого система автоматично обирає потрібні значення локальної адреси та процесу. Віддалений вузол визначається аргументом servaddr, який вказує на адресу серверові, а addrlen задає його довжину.

Виклик connect(2) може також використовуватися і клієнтами, які створюють сокети дейтаграм без створення віртуального каналу. У такому разі connect(2) не зреалізовує фактичного з’єднання з сервером, а може бути використаний для зберігання параметрів адреси сервера, якому буде спрямовано дейтаграми. Клієнт буде позбавлений від необхідності зазначати адресу серверові при кожному відправленні даних.

Системний виклик listen(2) інформує систему про готовність сервера приймати запитання. Він має такі параметри:

#include <sys/types.h>

#include <sys/socket.h>

int listen (int sockfd, int backlog);

Параметр backlog зазначає максимально можливу кількість запитань на встановлення зв’язку, які можуть очікувати, коли їх опрацює сервер. Якщо запит надходить, коли черга очікуючих запитів є повна, виклик connect(2) клієнта завершиться для домена UNIX (AF_UNIX) з помилкою ECONNRЕFUSED. Для інших доменів результат буде залежати від того, чи підтримує протокол повторне передавання запиту. Протокол ТСР (домен AF_INET) передаватиме повторні запити, допоки кількість запитів у черзі не зменшиться або не настане тайм-аут, окреслений для протоколу. У цьому разі виклик клієнта завершиться з помилкою ETIMEDOUT.

Фактичне опрацювання запиту клієнта на встановлення зв’язку зреалізовує системний виклик accept(2):

#include <sys/types.h>

#include <sys/socket.h>

int accept (int sockfd, struct sockaddr *clntaddr, *int addrlen);

Виклик accept(2) обирає перший запит з черги і створює новий сокет, характеристики якого не відрізняються від сокета sockfd і завершує створення віртуального каналу з боку сервера. Одночасно accept(2) повертає параметри віддаленого комунікаційного вузла — адресу клієнта clntaddr та його розмір addrlen. Новий сокет використовується для обслуговування створеного віртуального каналу, а отримана ним адреса клієнта виключає його анонімність. Типовий сценарій взаємодії має вигляд:

sockfd = socket(...); Створити сокет

bind (sockfd, …); Сполучити його з відомою локальною адресою

listen (sockfd, …); Організувати чергу запитів

for ( ; ; ) {

newsockfd = accept (sockfd, …) ; Отримати адресу

if /fork( ) = = 0 { ; Породити дочірній процес

close (sockfd) ; Дочірній процес

·

·

·

exit (0);

}

else

close (newsockfd); Батьківський процес

}

У цьому сценарії, у той час, коли дочірній процес забезпечує фактичний обмін даними з клієнтом, батьківський процес продовжує “прослуховувати” запити, котрі знову надходять, породжуючи для кожного з них окремий процес-опрацьовувач. Черга дозволяє буферизувати запити на той час, коли сервер завершує виклик accept(2), а потім створює дочірній процес. Новий сокет newsockfd, який створюється викликом accept(2), адресує повністю визначений комунікаційний канал: протокол та повні адреси обох вузлів — клієнта та сервера. Для сокета sockfd визначено лише локальну частину каналу. Це дозволяє серверові продовжувати використання sockfd для “прослуховування” наступних запитів.

Системні виклики listen(2) та accept(2) використовуються сервером лише в разі встановлення віртуального каналу поміж сервером та клієнтом.

Якщо для сокетів потоку під час приймання та передавання даних можуть використовуватися стандартні виклики read(2) та write(2), то сокети дейтаграм мають користуватися спеціальними системними викликами, які також є доступними для інших типів сокетів.

#include <sys/types.h>

#include <sys/socket.h>

int send (int s, const char *msg, int len, int flags);

int sendto (int s, const char *msg, int len, int flags,

const struct sockaddr *toaddr, int tolen);

int recv (int s, char *buf, int len, int flags);

int rеcvfrom (int s, char *buf, int len, int flags,

struct sockaddr *fromaddr, int * fromlen)

Функції send(2) та sendto(2) використовуються для передавання даних віддаленому вузлу, а функції recv(2) та rеcvfrom(2) — для їхнього приймання. Основною відміною поміж ними є те, що функції send(2) та recv(2) використовуються лише для “долученого” сокета, тобто після виклику connect(2).

Усі ці виклики використовують перший аргумент — дескриптор сокета, через який зреалізовується обмін даними. Аргумент msg вміщує повідомлення довжиною len, яке передається за адресою toaddr, довжина його становить tolen байтів. Для функції send(2) використовується адреса одержувача, встановлена попереднім викликом connect(2). Аргумент buf — це буфер, у який копіюються отримані дані.

Параметр flags може набирати таких значень:

MSG_OOB Передати або прийняти екстрені дані (out of band) замість звичайних

MSG_PEEK Переглянути дані без вилучення їх з системного буфера (наступні операції читання отримають ті ж самі дані)

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]