Програма-клієнт
Примечание. У режимі взаємодії без встановлення сполуки потреби у виконанні системного виклику connect немає. Проте, його виконання у тому режимі перестав бути помилкою — просто змінюється сенс виконуваних у своїй дій: встановлюється адресу «за умовчанням «для усіх наступних посилок дейтаграмм. Для встановлення «клієнт-сервер «використовуються системні виклики listen і accept (за серверу), і… Читати ще >
Програма-клієнт (реферат, курсова, диплом, контрольна)
Программа-клиент
Работа з WinSocket
Socket (гніздо, розняття) — абстрактне програмне поняття, що використовується для позначення в прикладної програмі кінцевої точки каналу зв’язки України із комунікаційним середовищем, освіченою обчислювальної мережею. При використанні протоколів TCP/IP можна говорити, що socket є способом підключення прикладної програми на порт (див. вище) локального вузла мережі.
Socket-интерфейс є просто набір системних викликів і/або бібліотечних функцій мови програмування СІ, розділених чотирма групи:
1. Локального управління.
2. Встановлення зв’язку.
3. Обміну даними (ввода/вывода).
4. Закриття зв’язку.
5. Приклад використання WinSocket.
Ниже розглядається підмножина функцій socket-интерфейса, достатні написання мережевих додатків, що реалізують модель «клієнт-сервер «як з впровадження сполуки.
1. Функції локального управління.
Функции локального управління використовуються, головним чином, до виконання підготовчих дій, необхідні організації взаємодії двох программ-партнеров. Функції носять таку назву, оскільки виконання носить локальний для програми характер.
1.1 Створення socket «а.
Создание socket «а здійснюється наступним системним викликом.
#include <sys/socket.h> int socket (domain, type, protocol) int domain; int type; int protocol;
Аргумент domain задає використовуваний для взаємодії набір протоколів (вид комунікаційної області), для стека протоколів TCP/IP він повинен мати символьне значення AF_INET (склала sys/socket.h).
Аргумент type задає режим взаимодействия:
SOCK_STREAM — з впровадження сполуки;
SOCK_DGRAM — без встановлення сполуки.
Аргумент protocolзадает конкретний протокол транспортного рівня (з кількох можливих, у стеці протоколів). Якщо це аргумент заданий рівним 0, він використаний протокол «за умовчанням «(TCP для SOCK_STREAM і UDP для SOCK_DGRAM під час використання комплекту протоколів TCP/IP).
При вдалому завершенні своєї роботи дана функція повертає дескриптор socket «а — ціле ненегативне число, однозначно його идентифицирующее. Дескриптор socket «а аналогічний дескриптору файла ОС UNIX.
При виявленні помилки у своїй роботи функція повертає число «-1 » .
1.2. Зв’язування socket «а.
Для підключення socket «а до комунікаційної середовищі, освіченою обчислювальної мережею, необхідні системний виклик bind, визначальний у прийнятому для мережі форматі локальний адресу каналу зв’язку з середовищем. У мережах TCP/IP socket пов’язують із локальним портом. Системний виклик bind має наступний синтаксис:
#include <sys/types.h>
#include <sys/socket.h> #include <netinet/in.h> int bind (p.s, addr, addrlen) int p. s; struct sockaddr *addr; int addrlen;
Аргумент p. s задає дескриптор связываемого socket «а.
Аргумент addr у випадку повинен вказувати на структуру даних, що містить локальний адресу, приписуваний socket «у. Для мереж TCP/IP такий структурою є sockaddr_in.
Аргумент addrlen задає розмір (в байтах) структури даних, указываемой аргументом addr.
Структура sockaddr_in використовується кількома системними викликами і функціями socket-интерфейса і в include-файле in. h наступним образом:
struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; };
Поле sin_family визначає використовуваний формат адреси (набір протоколів), у разі (для TCP/IP) вона повинна мати значення AF_INET.
Поле sin_addr містить адресу (номер) вузла сети.
Поле sin_port містить номер порту на вузлі сети.
Поле sin_zero не используется.
Определение структури in_addr (із такого самого include-файла) таково:
struct in_addr { union { u_long S_addr; /*.
інші (не цікаві для нас).
члени об'єднання */.
} S_un; #define s_addr S_un.S_addr };
Структура sockaddr_in мусить бути заповнена вщерть перед видачею системного виклику bind. У цьому, якщо полі sin_addr.s_addr має значення INADDR_ANY, то системний виклик буде прив’язувати до socket «у номер (адресу) локального вузла сети.
В разі успіху bind повертає 0, інакше — «-1 » .
2. Функції встановлення связи
Для встановлення «клієнт-сервер «використовуються системні виклики listen і accept (за серверу), і навіть connect (за клієнта). Для заповнення полів структури socaddr_in, яка у виклик connect, зазвичай використовується бібліотечна функція gethostbyname, транслирующая символічне ім'я вузла мережі у його номер (адрес).
2.1. Чекання встановлення связи Системный виклик listen висловлює бажання що видала його программы-сервера очікувати запити до неї від программ-клиентов і має наступний вид:
#include <sys/socket.h> int listen (p.s, n) int p. s; int n;
Аргумент p. s задає дескриптор socket «а ще через який програма чекатиме запити до неї від клієнтів. Socket може бути попередньо створено системним викликом socketи забезпечений адресою з допомогою системного виклику bind.
Аргумент n визначає максимальну довжину черги вхідних запитів встановлення зв’язку. Коли якійсь клієнт видасть запит встановлення зв’язку за цілковитої черги, цей запит буде отвергнут.
Признаком вдалого завершення системного виклику listen служить нульової код возврата.
2.2. Запит встановлення соединения Для звернення программы-клиента до сервера із запитом встановлення логічного сполуки використовується системний виклик connect, має наступний вид.
#include <sys/types.h>
#include <sys/socket.h> #include <netinet/in.h> int connect (p.s, addr, addrlen) int p. s; struct sockaddr_in *addr; int addrlen;
Аргумент p. s задає дескриптор socket «а ще через який програма звертається до сервера із запитом на з'єднання. Socket може бути попередньо створено системним викликом socketи забезпечений адресою з допомогою системного виклику bind.
Аргумент addr повинен вказувати на структуру даних, що містить адресу, приписаний socket «у программы-сервера, до котрої я робиться запит на з'єднання. Для мереж TCP/IP такий структурою є sockaddr_in. Щоб сформувати значень полів структури sockaddr_in зручно використовувати функцію gethostbyname.
Аргумент addrlen задає розмір (в байтах) структури даних, указываемой аргументом addr.
Для здобуття права запит на з'єднання був успішним, необхідно, по крайнього заходу, щоб програма-сервер виконала на цей момент системний виклик listen для socket «і з зазначеним адресом.
При успішне виконання запиту системний виклик connect повертає 0, інакше — «-1 «(встановлюючи код причини неуспіху у глобальній перемінної errno).
Примечание. Якщо на момент виконання connect використовуваний їм socket ні прив’язаний до адресою у вигляді bind, така прив’язка буде виконано автоматически.
Примечание. У режимі взаємодії без встановлення сполуки потреби у виконанні системного виклику connect немає. Проте, його виконання у тому режимі перестав бути помилкою — просто змінюється сенс виконуваних у своїй дій: встановлюється адресу «за умовчанням «для усіх наступних посилок дейтаграмм.
2.3. Прийом запиту встановлення связи Для прийому запитів від программ-клиентов на встановлення зв’язку в программах-серверах використовується системний виклик accept, має наступний вид:
#include <sys/socket.h>
#include <netinet/in.h> int accept (p.s, addr, p_addrlen) int p. s; struct sockaddr_in *addr; int *p_addrlen;
Аргумент p. s задає дескриптор socket «а ще через який програма-сервер отримала запит на з'єднання (у вигляді системного запиту listen).
Аргумент addr повинен вказувати галузь пам’яті, розмір якої допоміг би розмістити у ній структуру даних, що містить адресу socket «а программы-клиента, зробила запит на з'єднання. Ніякої ініціалізації цій галузі не требуется.
Аргумент p_addrlen повинен вказувати галузь пам’яті як цілого числа, задає розмір (в байтах) області пам’яті, указываемой аргументом addr.
Системный виклик accept витягує зі черги, організованою системним викликом listen, перший запит на з'єднання і повертає дескриптор нового (автоматично створеного) socket «і з тими самими властивостями, як і socket, задаваемый аргументом p. s. Цей новий дескриптор необхідно використовувати в усіх наступних операціях обміну данными.
Кроме того після вдалого завершення accept:
область пам’яті, указываемая аргументом addr, буде утримувати структуру даних (для мереж TCP/IP це sockaddr_in), описує адресу socket «а программы-клиента, з якого вона зробила свій запит на з'єднання;
целое число, яким вказує аргумент p_addrlen, дорівнюватиме розміру цієї структури даних.
Если чергу запитів на даний момент виконання accept порожня, то програма перетворюється на стан очікування надходження запитів від клієнтів невизначений час (така поведінка accept можна й изменить).
Признаком невдалого завершення accept служить негативне повернута значення (дескриптор socket «а негативним не может).
Примечание. Системний виклик accept використовують у программах-серверах, функціонуючих лише у режимі з впровадження соединения.
2.4. Формування адреси вузла сети Для отримання адреси вузла мережі TCP/IP з його символічному імені використовується бібліотечна функция.
#include <netinet/in.h>
#include <netdb.h> struct hostent *gethostbyname (name) char *name;
Аргумент name задає адресу послідовності літер, їхнім виокремленням символічне ім'я вузла сети.
При успішному завершенні функція повертає покажчик на структуру hostent, певну в include-файле netdb. h і має наступний вид.
struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_lenght; char *h_addr; };
Поле h_name свідчить про офіційне (основне) ім'я узла.
Поле h_aliases свідчить про список додаткових імен вузла (синонімів), якщо вони есть.
Поле h_addrtype містить ідентифікатор використовуваного набору протоколів, для мереж TCP/IP це полі матиме значення AF_INET.
Поле h_lenght містить довжину адреси узла.
Поле h_addr свідчить про область пам’яті, що містить адресу вузла у вигляді, у його використовують системні виклики і функції socket-интерфейса.
Пример звернення до функції gethostbyname щоб одержати адреси віддаленого вузла в программе-клиенте, використовує системний виклик connect на формування запиту на встановлення з'єднання з программой-сервером у цьому вузлі, розглядається ниже.
3. Функції обміну данными
В режимі з впровадження логічного сполуки після вдалого виконання пари взаємозалежних системних викликів connect (в клієнта) і accept (в сервері) стає можливим обмін данными.
Этот обмін може бути реалізовано звичайними системними викликами read і write, використовуваними до роботи з файлами (у своїй замість дескрипторів файлів у яких задаються дескриптори socket «ов).
Кроме того може бути додатково використані системні виклики send і recv, орієнтовані спеціально працювати з socket «ами.
Примечание. Для обміну даними як без встановлення логічного сполуки використовуються, зазвичай, системні виклики sendtoи recvfrom. Sendto дозволяє уточняти разом із переданими даними (складовими дейтаграмму) адресу їх одержувача. Recvfrom одночасно із доставкою даних одержувачу інформує його й про адресі отправителя.
3.1. Відправка данных Для посилки даних партнеру по мережному взаємодії використовується системний виклик send, має наступний вид.
#include <sys/types.h>
#include <sys/socket.h> int send (p.s, buf, len, flags) int p. s; char *buf; int len; int flags;
Аргумент p. s задає дескриптор socket «а ще через який посилаються данные.
Аргумент buf свідчить про область пам’яті, що містить передані данные.
Аргумент len задає довжину (в байтах) переданих данных.
Аргумент flags модифікує виконання системного виклику send. При нульовому значенні цього аргументу виклик send повністю аналогічний системному виклику write.
При успішному завершенні send повертає кількість переданих в галузі, зазначеної аргументом buf, байт даних. Якщо канал даних, визначається дескриптором p. s, виявляється «переповненим », то send переводить програму стан очікування досі його освобождения.
3.2. Одержання данных Для отримання даних від партнера по мережному взаємодії використовується системний виклик recv, має наступний вид.
#include <sys/types.h>
#include <sys/socket.h> int recv (p.s, buf, len, flags) int p. s; char *buf; int len; int flags;
Аргумент p. s задає дескриптор socket «а ще через який приймаються данные.
Аргумент buf свідчить про область пам’яті, призначену розміщувати прийнятих данных.
Аргумент len задає довжину (в байтах) цієї области.
Аргумент flags модифікує виконання системного виклику recv. При нульовому значенні цього аргументу виклик recv повністю аналогічний системному виклику read.
При успішному завершенні recv повертає кількість які у область, зазначену аргументом buf, байт даних. Якщо канал даних, визначається дескриптором p. s, виявляється «порожнім », то recv переводить програму стан очікування досі появи у ньому данных.
4. Функції закриття связи
Для закриття зв’язки України із партнером по мережному взаємодії використовуються системні виклики close і shutdown.
4.1. Системний виклик close.
Для закриття раніше створеного socket «а використовується звичайний системний виклик close, застосовуваний у ОС UNIX для закриття раніше відкритих файлів і має наступний вид.
int close (p.s) int p. s;
Аргумент p. s задає дескриптор раніше створеного socket «а.
Однако як з впровадження логічного сполуки (що забезпечує, зазвичай, надійну доставку даних) внутрішньосистемні механізми обміну намагатимуться передать/принять дані, які у каналі передачі на даний момент закриття socket «а. А ще може знадобитися значний інтервал часу, неприйнятний декому додатків. За такого стану необхідно використовувати описуваний далі системний виклик shutdown.
4.2. Скидання буферизованных данных Для «екстреного «закриття через відкликання партнером (шляхом «скидання «ще переданих даних) використовується системний виклик shutdown, що здійснюється перед close і має наступний вид.
int shutdown (p.s, how) int p. s; int how;
Аргумент p. s задає дескриптор раніше створеного socket «а.
Аргумент how задає дії, що їх при очищенні системних буферів socket «а:
0 — скинути і далі не приймати дані для читання з socket «а;
1 — скинути і далі не відправляти дані для посилки через socket;
2 — скинути всі дані, передані через socket в в будь-якому напрямку.
5. Приклад використання socket-интерфейса
В даному розділі розглядається використання socket-интерфейса як взаємодії з впровадження логічного сполуки на дуже простому прикладі взаємодії двох програм (серверу та клієнта), функціонуючих різними вузлах мережі TCP/IP.
Содержательная частина програм примітивна:
сервер, прийнявши запит на з'єднання, передає клієнту питання «Who are you? » ;
клиент, отримавши питання, виводить їх у стандартний висновок і направляє серверу відповідь «I am your client «і завершує на свою роботу;
сервер виводить у стандартний висновок відповідь клієнта, закриває з нею зв’язок і до стану очікування наступного запиту до нього.
Примечание. Запропоновані нижче тексти програм призначені для ілюстрації логіки взаємодії програм через мережу, у них відсутні такі атрибути програм, виділені на практичного застосування, як обробка кодів повернення системних викликів і функцій, аналіз кодів помилок у глобальній перемінної errno, реакція на асинхронні події та т.п.
5.1. Программа-сервер Текст программы-сервера мовою програмування СІ виглядає наступним образом.
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 #include <netinet/in.h>
4 #include <netdb.h>
5 #include <memory.h>
6 #define SRV_PORT 1234.
7 #define BUF_SIZE 64.
8 #define TXT_QUEST «Who are you? n «.
9 main () {.
10 int p. s, s_new;
11 int from_len;
12 char buf[BUF_SIZE];
13 struct sockaddr_in sin, from_sin;
14 p. s = socket (AF_INET, SOCK_STREAM, 0);
15 memset ((char *)&sin, «», sizeof (sin));
16 sin. sin_family = AF_INET;
17 sin. sin_addr.s_addr = INADDR_ANY;
18 sin. sin_port = SRV_PORT;
19 bind (p.s, (struct sockaddr *)&sin, sizeof (sin));
20 listen (p.s, 3);
21 while (1) {.
22 from_len = sizeof (from_sin);
23 s_new = accept (p.s, &from_sin, &from_len);
24 write (s_new, TXT_QUEST, sizeof (TXT_QUEST));
25 from_len = read (s_new, buf, BUF_SIZE);
26 write (1, buf, from_len);
27 shutdown (s_new, 0);
28 close (s_new);
29 };
30 }.
Строки 1…5 описують включаемые файли, містять визначення всім необхідних структур даних, і символічних констант.
Строка 6 приписує целочисленной константі 1234 символічне ім'я SRV_PORT. Надалі ця константа буде використано в ролі номери порту серверу. Значення цієї константи має бути відомо, і программе-клиенту.
Строка 7 приписує целочисленной константі 64 символічне ім'я BUF_SIZE. Ця константа визначатиме розмір буфера, використовуваного розміщувати прийнятих від клієнта данных.
Строка 8 приписує послідовності символів, складових текст питання клієнту, символічне ім'я TXT_QUEST. Останнім символом в послідовності є символ переходу нові рядок «n ». Зроблено це задля спрощення виведення тексту питання за клиента.
В рядку 14 створюється (відкривається) socket для організації режиму взаємодії з впровадження логічного сполуки (SOCK_STREAM) у мережі TCP/IP (AF_INET), під час виборів протоколу транспортного рівня використовується протокол «за умовчанням «(0).
В рядках 15…18 спочатку обнуляется структура даних sin, та був заповнюються її окремі поля. Використання константи INADDR_ANY спрощує текст програми, позбавляючи необхідності використовувати функцію gethostbyname щоб одержати адреси локального вузла, у якому запускається сервер.
Строка 19 у вигляді системного виклику bind прив’язує socket, задаваемый дескриптором p. s, на порт з номером SRV_PORT на локальному вузлі. Bind завершиться успішно за умови, зараз його виконання тому ж вузлі не функціонує програма, яка використовує цей номер порта.
Строка 20 у вигляді системного виклику listen організує чергу втричі вхідних до сервера запиту на соединение.
Строка 21 служить заголовком нескінченного циклу обслуговування запитів від клиентов.
На рядку 23, що містить системний виклик accept, виконання програми припиняється невизначений час, якщо чергу запитів до сервера встановлення зв’язку стає порожня. За появи такого запиту accept успішно завершується, повертаючи в перемінної s_new дескриптор socket «а обміну інформацією між з клиентом.
В рядку 24 сервер з допомогою системного виклику write відправляє клієнту вопрос.
В рядку 25 з допомогою системного виклику read читається відповідь клиента.
В рядку 26 відповідь направляють у стандартний висновок, має дескриптор файла номер 1. Оскільки рядок відповіді містить у собі символ переходу нові рядок, то текст відповіді буде розміщений на окремої рядку дисплея.
Строка 27 містить системний висновок shutdown, який би очищення системних буферів socket «а, містять дані для читання («зайві «дані можуть там приєднатися до результаті зрадливої роботи клиента).
В рядку 28 закривається (видаляється) socket, використаний обмінюватись даними з черговою клиентом.
Примечание. Ця програма (як більшість реальних программ-серверов) самостійно своєї роботи завершує, перебуваючи в нескінченному циклі обробки запитів клієнтів. Виконання їх то, можливо перервано лише ззовні шляхом посилки їй сигналів (переривань) завершення. Правильно розроблена програма-сервер повинна обробляти такі сигнали, коректно завершуючи роботу (закриваючи, зокрема, у вигляді close socket з дескриптором s).
Текст программы-клиента мовою програмування СІ виглядає наступним образом.
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 #include <netinet/in.h>
4 #include <netdb.h>
5 #include <memory.h>
6 #define SRV_HOST «delta «.
7 #define SRV_PORT 1234.
8 #define CLNT_PORT 1235.
9 #define BUF_SIZE 64.
10 #define TXT_ANSW «I am your clientn «.
11 main () {.
12 int p. s;
13 int from_len;
14 char buf[BUF_SIZE];
15 struct hostent *hp;
16 struct sockaddr_in clnt_sin, srv_sin;
17 p. s = socket (AF_INET, SOCK_STREAM, 0);
18 memset ((char *)&clnt_sin, «», sizeof (clnt_sin));
19 clnt_sin.sin_family = AF_INET;
20 clnt_sin.sin_addr.s_addr = INADDR_ANY;
21 clnt_sin.sin_port = CLNT_PORT;
22 bind (p.s, (struct sockaddr *)&clnt_sin, sizeof (clnt_sin));
23 memset ((char *)&srv_sin, «», sizeof (srv_sin));
24 hp = gethostbyname (SRV_HOST);
25 srv_sin.sin_family = AF_INET;
26 memcpy ((char *)&srv_sin.sin_addr, hp->h_addr, hp->h_length);
27 srv_sin.sin_port = SRV_PORT;
28 connect (p.s, &srv_sin, sizeof (srv_sin));
29 from_len = recv (p.s, buf, BUF_SIZE, 0);
30 write (1, buf, from_len);
31 send (p.s, TXT_ANSW, sizeof (TXT_ANSW), 0);
32 close (p.s);
33 exit (0);
34 }.
В рядках 6 і аналогічних сім описуються константи SRV_HOST і SRV_PORT, що визначають ім'я віддаленого вузла, у якому функціонує програма-сервер, і номер порту, якого прив’язаний socket сервера.
Строка 8 приписує целочисленной константі 1235 символічне ім'я CLNT_PORT. Надалі ця константа буде використано в ролі номери порту клиента.
В рядках 17…22 створюється прив’язаний на порт на локальному вузлі socket.
В рядку 24 у вигляді бібліотечної функції gethostbyname транслюється символічне ім'я віддаленого вузла (у разі «delta »), у якому має сервер, на адресу цього вузла, розміщений у структурі типу hostent.
В рядку 26 адресу віддаленого вузла копіюється з структури типу hostent до відповідного полі структури srv_sin, яка згодом (в рядку 28) використовують у системному виклик connect для ідентифікації программы-сервера.
В рядках 29…31 здійснюється обмін даними з сервером та виведення питання, надходження від серверу, у стандартний вывод.
Строка 32 у вигляді системного виклику close закриває (видаляє) socket.
Список литературы
Для підготовки даної роботи було використані матеріали із російського сайту internet.