- •Практичне заняття 10
- •Сокети (sockets) в unix і основи роботи з ними.
- •Системні виклики sendto() і recvfrom()
- •Визначення ip-адрес для обчислювального комплексу
- •Приклад програми udp-клієнта
- •Приклад програми udp-серверу
- •Організація зв'язку між процесами за допомогою установки логічного з'єднання
- •Встановлення логічного з'єднання. Системний виклик connect()
- •Приклад програми tcp-клієнта
- •Як відбувається встановлення віртуального з'єднання
- •Системний виклик listen()
- •Системний виклик accept()
- •Приклад простого tcp-серверу
- •Вживання інтерфейсу мережних викликів для інших сімейств протоколів. Unix Domain протоколи. Файли типу "сокет"
Вживання інтерфейсу мережних викликів для інших сімейств протоколів. Unix Domain протоколи. Файли типу "сокет"
Розглянутий нами інтерфейс уміє працювати не тільки із стеком протоколів TCP/IP, але і з іншими сімействами протоколів. При цьому потрібна лише незначна зміна написаних з його допомогою програм. Розглянемо дії, які необхідно виконати для модернізації написаних для TCP/IP програм під інше сімейство протоколів.
Змінюється тип сокета, тому для його точної специфікації потрібно задавати інші параметри в системному виклику socket().
В різних сімействах протоколів застосовуються різні адресні простори для видалених і локальних адрес сокетів. Тому міняється склад структури для зберігання повної адреси сокета, назва її типу, найменування полів і спосіб їх заповнення.
Опис типів даних і приречених констант знаходитиметься в інших include-файлах, тому буде потрібно замінити include-файли <netinet/in.h> і <arpa/inet.h> на файли, що відносяться до вибраного сімейства протоколів.
Може змінитися спосіб обчислення фактичної довжини повної адреси сокета і вказівки його максимального розміру.
Мал. 15-16.9. Схема роботи TCP-серверу з паралельною обробкою запитів
Давайте докладніше розглянемо ці зміни на прикладі сімейства UNIX Domain протоколів. Сімейство UNIX Domain протоколів призначено для спілкування локальних процесів з використанням інтерфейсу системних викликів. Воно містить один потоковий і один датаграмний протокол. Ніякий мережний інтерфейс при цьому не використовується, а вся передача інформації реально відбувається через адресний простір ядра операційної системи. Багато програм, що взаємодіють і з локальними, і з видаленими процесами (наприклад, X-Windows), для локального спілкування використовують цей стек протоколів.
Оскільки спілкування відбувається в рамках однієї обчислювальної системи, в повній адресі сокета його видалена частина відсутня. Як адресний простір портів – локальної частини адреси – вибрано адресний простір, співпадаючий з безліччю всіх допустимих імен файлів у файловій системі.
При цьому як ім'я сокета вимагається задавати ім'я неіснуючого ще файлу в директорії, до якої у вас є права доступу як на запис, так і на читання. При настройці адреси (системний виклик bind()) під цим ім'ям буде створений файл типу "сокет" – останній ще невідомий нам тип файлу. Цей файл для сокетів грає роль файлу-мітки типа FIFO для іменованих pip’ов. Якщо на вашій машині функціонують X-Windows, то ви зможете знайти такий файл в директорії з ім'ям /tmp/.X11-unix – це файл типу "сокет", що служить для взаємодії локальних процесів з віконним сервером.
Для зберігання повної адреси сокета використовується структура наступного вигляду, описаного у файлі <sys/un.h>:
struct sockaddr_un{
short sun_family;
/* Вибране сімейство
протоколів – завжди AF_UNIX */
char sun_path[108];
/* Ім'я файлу типу "сокет" */
};
Вибране ім'я файлу ми копіюватимемо всередину структури, використовуючи функцію strcpy().
Фактична довжина повної адреси сокета, що зберігається в структурі з ім'ям my_addr, може бути обчислена таким чином: sizeof(short)+strlen(my_addr.sun_path). В Linux для цих цілей можна використовувати спеціальний макрос мови З
SUN_LEN(struct sockaddr_un*)
Нижче приведені тексти переписаних під сімейство UNIX Domain протоколів клієнта і серверу для сервісу echo (програми 15–16-5.c і 15–16-6.c), що спілкуються через датаграми. Клієнт використовує сокет з ім'ям AAAA в поточній директорії, а сервер – сокет з ім'ям BBBB. Як випливає з опису типу даних, ці імена (повні або відносні) не повинні по довжині перевищувати 107 символів. Коментарі дані лише для змін в порівнянні з програмами 15–16-1.c і 15–16-2.c.
/* А simple echo UNIX Domain datagram server */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h> /* Новий include-файл замість netinet/in.h і arpa/inet.h */
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
int main()
{
int sockfd;
int clilen, n;
char line[1000];
struct sockaddr_un servaddr, cliaddr; /* новий тип даних під адреси сокетів */
if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
/* Змінений тип сімейства протоколів */
{
реrror(NULL);
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX; /* Змінений тип сімейства протоколів і ім'я поля в структурі */
strcpy(servaddr.sun_path,"BBBB"); /* Локальний адреса сокета серверу – BBBB – в поточній директорії */
if(bind(sockfd (struct sockaddr *) &servaddr
SUN_LEN(&servaddr)) < 0) /* Змінено обчислення фактичної довжини адреси */
{
реrror(NULL);
close(sockfd);
exit(1);
}
while(1){
clilen = sizeof(struct sockaddr_un); /* Змінено обчислення максимальної довжини для адреси клієнта */
if((n = recvfrom(sockfd, line, 999, 0
(struct sockaddr *) &cliaddr &clilen)) < 0){
реrror(NULL);
close(sockfd);
exit(1);
}
if(sendto(sockfd, line, strlen(line), 0
(struct sockaddr *) &cliaddr, clilen) < 0){
реrror(NULL);
close(sockfd);
exit(1);
}
}
return 0;
}
Лістинг 15-16.5. Програма 15–16-5.c . А simple echo UNIX Domain datagram server
/* А simple echo UNIX Domain datagram client */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h> /* Новий include-файл замість netinet/in.h і arpa/inet.h */
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
int main() /* Аргументи командного рядка не потрібні оскільки сервіс є локальним, і не потрібно указувати, до якої машини ми поводимося із запитом */
{
int sockfd;
int n, len;
char sendline[1000], recvline[1000];
struct sockaddr_un servaddr, cliaddr; /* новий тип даних під адреси сокетів */
if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) /* Змінений тип сімейства протоколів */
{
реrror(NULL);
exit(1);
}
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sun_family= AF_UNIX; /* Змінений тип сімейства протоколів і ім'я поля в структурі */
strcpy(cliaddr.sun_path,"AAAA");/* Локальна адреса сокета клієнта – AAAA – в поточній директорії */
if(bind(sockfd (struct sockaddr *) &cliaddr SUN_LEN(&cliaddr)) < 0) /* Змінено обчислення фактичної довжини адреси */
{
реrror(NULL);
close(sockfd);
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX; /* Змінений тип сімейства протоколів і ім'я поля в структурі */
strcpy(servaddr.sun_path,"BBBB"); /* Локальна адреса сокета серверу – BBBB – в поточній директорії */
printf("String => ");
fgets(sendline, 1000, stdin);
if(sendto(sockfd, sendline, strlen(sendline)+1 0 (struct sockaddr *) &servaddr SUN_LEN(&servaddr)) < 0) /* Змінено обчислення фактичної довжини адреси */
{
реrror(NULL);
close(sockfd);
exit(1);
}
if((n = recvfrom(sockfd, recvline, 1000, 0 (struct sockaddr *) NULL, NULL)) < 0){
реrror(NULL);
close(sockfd);
exit(1);
}
recvline[n]= 0;
printf("%s", recvline);
close(sockfd);
return 0;
}
Лістинг 15-16.6. Програма 15–16-6.c . А simple echo UNIX Domain datagram client.
Наберіть програми, відкомпілюйте їх і переконайтеся в працездатності.
