ТОП 10:

Робота з потоковими сонетами



Тут розглядатимуться головні системні виклики, які використовують під час роз­робки серверів та клієнтів з використанням потокових сокетів на основі протоколу TCP (рис. 16.4). Це - основний вид сокетів, що найчастіше трапляється в реаль­них застосуваннях.

 

Зв'язування сокета з адресою

Сокет, дескриптор якого повернутий викликом socket О, не пов'язаний із кон­кретною адресою і недоступний для клієнтів. Для пов'язування сокета з адресою необхідно використати системний виклик bind():

 

int bind(int sockfd. const struct sockaddr *pmy_addr. socklen_t alen):

де: sockfd — дескриптор сокета, створений за допомогою socket();

pmyaddr — покажчик на заздалегідь заповнену структуру, що задає адресу со­кета (наприклад, sockaddrin);

 

alen - розмір структури, на яку вказує pmyaddr.

 

Цей виклик повертає -1, якщо виникла помилка.

Під час виконання bind О застосуванням-сервером необхідно задати наперед відомий порт, через який клієнти зв'язуватимуться з ним. Можна також вказати конкретну IP-адресу (при цьому допускатимуться лише з'єднання, для яких вона зазначена як адреса призначення), але найчастіше достатньо взяти довільну адре­су локального хоста, скориставшись константою INADDRANY:

 

struct sockaddMn my_addr = { 0 };

int listenfd – socket(...);

// ... задача my_addr.sin_family i my_addr.sin_port

my_addr.sin_addr.s_addr = INADDR_ANY;

bind(listenfd. (struct sockaddr *)&my_addr. sizeof(my_addr));

Найпоширенішою помилкою під час виклику bind() є помилка з кодом EADDRI-NUSE, яка свідчить про те, що цю комбінацію «IP-адреса — номер порту» вже вико­ристовує інший процес. Часто це означає повторну спробу запуску того самого сервера.

 

if (binddistenfd. ...)== -1 && errno == EADDRINUSE)

{ printfC'noMMnKa, адресу вже використовують\п"); exit(-l); }

 

Щоб досягти гнучкості у вирішенні цієї проблеми, рекомендують дозволяти користувачам налаштовувати номер порту (задавати його у конфігураційному файлі, командному рядку тощо).

Відкриття сокета для прослуховування

За замовчуванням сокет, створений викликом socketO є клієнтським активним сокетом, за допомогою якого передбачають з'єднання із сервером (особливості використання таких сокетів розглянемо в наступному розділі). Щоб перетворити такий сокет у серверний прослуховувальний (listening), призначений для при­ймання запитів на з'єднання від клієнтів, необхідно використати системний ви­клик listen()

 

int listenCint sockfd. int backlog):

 

де: sockfd - дескриптор сокета, пов'язаний з адресою за допомогою bindO;

backl og - задає довжину черги запитів на з'єднання, створеної для цього сокета.

Фактично внаслідок виклику listen О створяться дві черги запитів: перша з них містить запити, для яких процес з'єднання ще не завершений, друга — запи­ти, для яких з'єднання встановлене. Параметр backlog визначає сумарну довжину цих двох черг; для серверів, розрахованих на значне навантаження, рекоменду­ють задавати достатньо велике значення цього аргументу:

 

listentlistenfd. 1024):

Внаслідок виклику listen О сервер стає цілковито готовий до приймання за­питів на з'єднання.

 

Прийняття з'єднань

Спроба клієнта встановити з'єднання із сервером після виклику listenО має за­вершуватися успішно. Після створення нове з'єднання потрапляє у чергу вста­новлених з'єднань. Проте, для сервера воно залишається недоступним. Щоб от­римати доступ до з'єднання, на сервері його потрібно прийняти за допомогою системного виклику acceptO:

 

int acceptant sockfd. struct sockaddr *cliaddr. socklen_t *addrlen);

де: sockfd - дескриптор прослуховувального сокета;

cliaddr - покажчик на структуру, у яку буде занесена адреса клієнта, що за­просив з'єднання;

addrlen - покажчик на змінну, котра містить розмір структури cliaddr.

 

Головною особливістю цього виклику є повернене значення — дескриптор но­вого сокета з'єднання (connection socket), який створений внаслідок виконання цього виклику і призначений для обміну даними із клієнтом. Прослуховувальний сокет після виконання accepte) готовий приймати запити на нові з'єднання.

 

struct sockaddMn their_addr = { 0 };

int listenfd. connfd, sin_size = sizeof(struct sockaddrin);

// listenfd « sockett...). bindelistenfd. ...). 1 іsten(listenfd. ...)

connfd = aeeeptilistenfd. (struct sockaddr *)&their_addr. &sin_size);

// використання connfd для обміну даними із клієнтом

 

Виклик acceptO приймає з'єднання, що стоїть першим у черзі встановлених з'єднань. Якщо вона порожня, процес переходить у стан очікування, де перебува­тиме до появи нового запиту. Саме на цьому системному виклику очікують ітера­тивні та багатопотокові сервери, що використовують сокети. Такі сервери розгля­датимемо далі в цьому розділі.

 

Задання з'єднань на клієнті

Дотепер ми розглядали виклики, які мають бути використані на сервері. На клі­єнті ситуація виглядає інакше. Після створення сокета за допомогою socket ( ) не­обхідно встановити для нього з'єднання за допомогою системного виклику con­necte ), після чого можна відразу обмінюватися даними між клієнтом і сервером.

int connect(int sockfd. const_struct sockaddr *saddr. socklen_t alen); де: sockfd - сокет, створений за допомогою socketí);

saddr — покажчик на структуру, що задає адресу сервера (IP-адресу віддалено­го хоста, на якому запущено сервер і порт, який прослуховує сервер); alen - розмір структури saddr. Ось приклад створення з'єднання на клієнті:

struct sockaddrJn their_addr - { 0 };sockfd - sockett...);

// заповнення their_addr.sin_family. their_addr.sin_port inet_aton("IP-aapeca-сервера". &(their_addr.sin_addr)); connectesockfd. (struct sockaddr *)&their_addr. sizeof(their_addr)); // обмін даними через sockfd

У разі виклику connecte ) починають створювати з'єднання. Повернення відбу­вається, якщо з'єднання встановлене або сталася помилка (при цьому поверне­ним значенням буде -1). Зазначимо, що, якщо connecte ) повернув помилку, кори­стуватися цим сокетом далі не можна, його необхідно закрити. Коли потрібно повторити спробу, створюють новий сокет за допомогою socket ( ).

Клієнтові зазвичай немає потреби викликати binde ) перед викликом connecte ) -ядро системи автоматично виділяє тимчасовий порт для клієнтського сокета.

 

Закриття з'єднань

Після використання всі з'єднання необхідно закривати. Для цього застосовують стандартний системний виклик close() :

close(sockfd);

 







Последнее изменение этой страницы: 2017-02-06; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.206.187.81 (0.004 с.)