Когда-то компьютеры были очень громоздкими, и их было очень мало, а на один большой компьютер приходилось много пользователей с еще большим числом очень маленьких задач, вопрос о перенесении программ и данных с одного компьютера на другой не играл какой-либо заметной роли.
Однако по мере совершенствования компьютеров, увеличения их числа, развития и усложнения средств программного обеспечения, в том числе и прикладного, увеличения объемов баз данных возникла насущная необходимость в соединении компьютеров между собой. Такое связывание компьютеров, позволяющее объединить их ресурсы — процессоры, память (внутреннюю и внешнюю, включая жесткие диски, разнообразные внешние устройства — принтеры, факс-аппараты, модемы и др.), каналы связи, и представляет собой вычислительную сеть, в которой каждый компьютер может передать другому компьютеру, подключенному к сети, любой набор данных.
Таким образом, вычислительная сеть, объединяющая ресурсы нескольких компьютеров, позволяет каждому из них использовать всю совокупность этих ресурсов.
Клиент-сервер архитектура простыми словами. Что такое клиент — сервер? | 2022
Вычислительные сети строятся на основе архитектуры «клиент-сервер», которая предполагает выделение в сети так называемых «серверов» и «клиентов». К клиентам относятся рабочие станции (компьютеры) сети, которые не имеют непосредственных контактов друг с другом и могут общаться между собой только через сервер: следовательно, одна станция не может использовать файл, находящийся на другой станции: для этого файл должен находиться на файл-сервере. Серверы управляют подключенными к ним общими разделяемыми ресурсами сети. В качестве сервера может быть использован либо обычный персональный компьютер, или же это может быть специализированное устройство.
На протяжении последних десяти лет специалисты по вычислительной технике работают над усовершенствованием приложений клиент-сервер. В результате были построены приложения, поддерживающие совместную работу множества пользователей с единственным источником данных в сети.
Архитектура клиент-сервер стала общераспространенной при общении с компьютером или с системой на его основе. Любой человек, подключающийся к диалоговой информационной системе с помощью телефонной связи, использует архитектуру клиент-сервер. Пользуясь автоматическим кассовым аппаратом, считывая штриховые коды своих покупок на проверочном устройстве магазина или расплачиваясь за них с помощью кредитной карточки, идет взаимодействие с компьютерной системой клиент-сервер.
В ходе выполнения курсового проекта необходимо изучить архитектуру клиент-сервер, организацию взаимодействия между клиентом и сервером и разработать программное обеспечение, представляющее собой удаленный генератор псевдослучайных последовательностей.
ТЕОРЕТИЧЕСКАЯ ЧАСТЬ
Согласно онлайновому словарю компьютерных терминов, клиент-сервер — это вид распределенной системы, в которой есть сервер, выполняющий запросы клиента, причем сервер и клиент общаются между собой с использованием того или иного протокола.
Клиент-серверная архитектура в картинках
Под клиентом понимается программа, использующая ресурсы, а под сервером (по английски — слуга) программа, обслуживающая запросы клиентов на получение ресурсов определенного вида. Столь широкое определение включает в себя практически любую программную технологию, в которой участвуют больше одной программы, функции между которыми распределены асимметрично. Соответственно, говорят о технологии КС применителько к операционным системам, локальным и глобальным сетям и т. д.
Такое широкое определение рождает некоторую путаницу. Так, файл-серверная система тоже использует технологию клиент-сервер, однако с точки зрения архитектуры прикладных программ важным является то, какого рода ресурсы сервер предоставляет клиентам.
Понятие архитектуры клиент-сервер в системах управления предприятием связано с делением любой прикладной программы на три основных компонента или слоя. Этими тремя компонентами являются:
компонент представления (визуализации) данных;
компонент прикладной логики;
компонент управления базой данных.
Действительно, любая программа, компьютеризирующая выполнение той или иной прикладной задачи, должна обмениваться информацией с пользователем, осуществлять собственно обработку этой информации в рамках автоматизации того или иного бизнес-процесса, и, наконец, хранить данные используемые в программе, на том или ином постоянном носителе.Для локальных приложений, полностью работающих на ПЭВМ (например, Word или Excel), все эти компоненты собраны вместе и не могут быть распределены между различными компьютеры. Такая программа является монолитной и использует для выполнения ресурсы только того компьютера, на котором выполняется.
В файл-серверных приложениях часть компоненты хранения переносится на файловый сервер, однако, все манипуляции со структурами данных выполняются на клиентской машине, и код пользовательской программы тоже работает только на ней.Критерием, позволяющим отнести прикладную программы к архитектуре клиент-сервер является то, что хотя бы один из трех ее компонентов полностью выполняется на другом компьютере, и взаимодействие между компонентами на разных компьютерах осуществляется через ту или иную сетевую среду посредством передачи запросов на получение того или иного ресурса.
Различие между сервером и клиентом существенно только тогда, когда клиент пытается подключиться к серверу. Как только они соединятся, происходит процесс двухстороннего общения и не важно, что один является сервером, а другой — клиентом.
Итак, работа сервера — слушать соединение, и это выполняется с помощью специального создаваемого серверного объекта (сокета), который равен: сокет = IP + номер_порта. Работа клиента — попытаться создать соединение с сервером, что выполняется с помощью специального клиентского объекта (сокета). Как только соединение установлено, соединение превращается в потоковый объект ввода/вывода, и с этого момента можно рассматривать соединение как файл, который Вы можете читать, и в который Вы можете записывать данные. Единственная особенность, файл может обладать определенным интеллектом и обрабатывать передаваемые Вами команды.
Сокет(гнездо, разъем) (сокет=IP+ПОРТ) — это программная абстракция, используемая для представления “терминалов” соединений между двумя машинами. Для такого соединения, существует сокет на каждой машине, и можно представить себе виртуальный “кабель” соединяющий две машины, каждый конец которого вставлен в сокет.
Как известно в операционной системе Windows связь между разными процессами производится при помощи сокетов (Sockets). Соединяя вместе два сокета, можно передавать данные между разными процессами (локальными и удаленными). Реализация сокетов обеспечивает инкапсуляцию протоколов сетевого и транспортного уровней. Интерфейс , используемый при этом взаимодействии, называется Sockets API. Он представлен библиотекой WinSock и java.net.*.
ОСНОВНАЯ ЧАСТЬ
1Постановка задачи
Разработать клиент серверное-приложение на основе TCPIP соединения.
Организовать работу удаленного генератора псевдослучайных последовательностей. Функции в программе: одно целое число в диапазоне, массив чисел в диапазоне.
После подключения к серверу клиента у пользователя будет возможность работать с псевдослучайными последовательностями воспользовавшись при этом русскоязычным удобным меню. Пользователь сможет воспользоваться следующими функциями, которые предоставляет сервер.Выбор диапазона чисел, вывод массива чисел либо одного числа. После выбора пользователем методов вывода значений, клиент отправляет информацию на сервер. Там она обрабатывается и возвращается обратно уже готовым результатом.
После подключения клиента к серверу,пользователь указывает диапазон значений и их количество и посылает серверу запросы псевдослучайных последовательностей. Сервер принимает значения, обрабатывает и отправляет обратно готовый результат.
Работа была выполнена на языке программирования JAVA в среде разработки Eclipse.
В работе используются библиотека java.net.*; разработанная компанией Sun, которая упрощает разработку по технологии клиент – сервер.
2Моделирование и реализация ПО
2.1 АрхитектураПО
Приложениереализовано по технологии клиент-сервер с использованием сокетов Windows, соответственно разрабатываются приложения: клиента и сервера. Приложение «Сервер» запускается в одном экземпляре и выполняет прослушивание заданного порта на предмет подключения клиента. После запроса на подключение сервер устанавливает соединение с клиентом и обрабатывает его запросы. Приложение «Клиент» может быть запущено в нескольких экземплярах.Клиенты подключаются к серверу и посылаютему запросы псевдослучайных последовательностей.Принятые от сервера последовательности отображаются в главном окне. Схема работы программного обеспечения представлена на рисунке 1.
2.2 Описание основных функциональных модулей
Функциональный модуль «Клиент».
Модуль предоставляет текстовый интерфейс для взаимодействия с пользователем. Позволяет выбрать порт сервера, к которому необходимо подключаться для запроса псевдослучайных последовательностей. Также в главном окне модуля есть возможность выбрать тип запроса: одно число или массив чисел, границы для случайных чисел и их количество.
Функциональный модуль «Сервер».
Модуль отвечает за установление соединения с каждым новым клиентом. После соединения с клиентом модуль принимает запрос от клиента, после чего он выполняет запрошенную функцию (генерацию одного или массива псевдослучайных чисел) и возвращает клиенту полученные числа.
2.2.1 Интерфейс пользователя
Для взаимодействия с пользователем в приложении используется оконный интерфейс. В текстовом, консольном режиме.
Главное меню приложения клиента на рисунке 2.
Нужное меню выбирает от 1 до 4. Номер функции отправляется серверу, он обрабатывает ее и высылает результат клиенту.
2.2.2. Прикладной компонент
В данном программном обеспечении прикладной функцией является генерация псевдослучайной последовательности чисел. Эта задача реализована в рамках сервера. Сервер в ответ на запрос клиента формирует последовательность чисел заданной длины, используя функцию java.util.Random() для генерации каждого из чисел. Сгенерированное функцией псевдослучайное число приводится затем к заданному диапазону.
2.2.3. Организация сетевого взаимодействия
Сетевое взаимодействие происходит посредством передачи и приема данных через сокеты, связывающие клиента и сервера. После установления соединения с сервером клиент по запросу пользователя передает запрос последовательности чисел. Формат запроса приведен ниже:
Ввод последовательности ->запись в переменные на сервере.
Выбор нужного действия -> передача нужной функции на сервер ->ответ от сервера.
2.3. Реализация ПО
Алгоритм программы клиента рисунок 4.
Алгоритм программы сервера рисунок 5.
Работа с сокетами представлена классами ServerSocket. Класс, используемый сервером, чтобы “слушать” входящие соединения и Socket- используемый клиентом для инициирования соединения. Как только клиент создает соединение по сокету, ServerSocket возвращает ему с помощью метода accept()соответствующий объект Socket на сервере, по которому будет происходить связь со стороны сервера. Начиная с этого момента, появляется соединение Socket к Socket, и можно считать эти соединения одинаковыми, т.к. они действительно одинаковые.
3Руководство пользователя
Для удобства пользователю предоставляется русскоязычное меню. Нужно выбрать нужный вариант из списка и следовать дальнейшему меню.
Вводить параметры для запроса последовательности псевдослучайных чисел и
нажимать кнопку «Enter». В окне должны отображаться получаемые от сервера псевдослучайные числа.
Источник: student-files.ru
1.2 Разработка программ в архитектуре “клиент-сервер”
Большинство прикладных программ является не единым программным модулем, а набором взаимодействующих между собой подпрограмм , одни из которых являются модулями ОС, а могли вообще принадлежать другим разработчикам – их надо смело поставлять и устанавливать, например, из Интернет. В прикладной программе можно выделить два уровня:
- Нижний, отвечающий за методы хранения доступа, разделения данных,— поставляемый
- Верхний, отвечающий за логику обработки данных и интерфейс пользователя –собой.
Первый уровень часто связан с базами данных и доступом к ним, и для работы первого уровня часто требовалося наличие высокопроизводительной системы. Второй не требовал наличия высокопроизводительной системы вычислений и включая в себе собственно алгоритм логику и весь интерфейс, созданный разработчиком программы. Так сложилось понятие приложения, построенного на архитектуре «клиент -сервер». Первый уровень, сервер, включает методы, связанные с доступом данных; их реализует сервер БД из соответствующей СУБД в комплекте с драйверами доступа к телу. Второй уровень, клиент, взаимодействует, с одной стороны, с сервером, получая от него данные, а с другой стороны – с пользователем, ресурсами приложения и ОС, осуществляя обработку данных и отображение результатов. Результаты обработки клиент опять-таки может сохранить в обработку данных и отображение результатов. Результаты обработки клиент опять-таки может сохранить в БД, используя функции серверной части. Многие известные фирмы стали предлагать стандартные интерфейсы для доступа к создаваемым им СУБД. Изменилась и структура систем программирования; они стали ориентироваться на создание в архитектуре, клиент — сервер. Предлагаемые или средства поставляются в составе систем программирования и поддерживают возможность работы с широким диапазоном известных серверов данных через один или несколько доступных интерфейсов обмена данными. Разработчик прикладной программы выбирает одно из доступных средств и возможный тип сервера. Тогда задача сводится только к созданию клиентской части приложения, построенной на основе выбранного интерфейса. В дальнейшем использовать приложение можно только в комплексе. Интерфейс обмена данными входит в систему программирования и свободно распространяется. Серверная часть распространяется только по лицензиям, надо лицензировать средства создания и отладки БД, но результаты работы могут распространяться. Либо в случае мощных серверов, лицензия нужна на и распространение серверной части приложение. В этом случае конечный пользователь получает комплекс программы, продуктов от множества разработчиков. Развитием архитектуры программирования «клиент – сервер» явилась трехуровневая архитектура. Клиентская часть разделилась на две составляющие: сервер приложений и «тонкий клиент, обеспечивающий интерфейс доступ к результатам обработки. Серверная часть, сервер без данных, осталась без изменений. Взаимодействие между сервером приложений и тонким клиентом должно быть тесным, а, с другой стороны, должен обмениваться данными по протоколам интернет, которые стандартизованы, например, COM/DCOM, CORBA и другие (COMMON Object Reguest Broklr Architecture) Пример простого ТСР – клента
- #include
- #include
- #include
- #include
- #include
- int main (void)
- struct sockaddr_in peer;
- int d;
- int rc;
- char buf [ 1 ];
- peer.sin_family = AF_INET;
- peer.sin_pott = Atons( 7500 );
- peer.sin_addr.s addr = inet_addr(“127.0.0.1”);
- S = socket (AF_INET, SOCK_STREAM, 0 );
- If ( s < 0)
- perror( «ошибка вызова sockett»);
- exit ( 1 );
- >
- rc=connect ( s, ( struct sockaddr * )
- if ( rc
- perror ( «ошибка вызова connect» );
- exit ( 1 );
- >
- rc = send ( s, “1”, 1, 0 );
- if ( rc
- perror («ошибка вызова send» );
- exit ( 1 );
- >
- rc = recv( s, buf, 1, 0 );
- if ( rc
- perror( «ошибка вызова reccv» );
- else
- printf ( “%n”, buf [ 0 ];
- exit( 0 );
- >
Подготовка адреса сервера: 12 – 14 Заполнение структуры sockaddr_in путем записи в ее поля номера порта (7500) и адреса 127.0.0.1 – это возвратный адрес, который означает, что сервер находится на той же машине, что и клиент. Получение сокета и соединение с сервером: 15 – 20 Получение сокета типа SOCK_STREAM, т.к. используется потоковый протокол ТСР. 21 – 26 Установка соединения с сервером путем обращения к системному вызову connect. Это вызов нужен, чтобы сообщить ядру адрес сервера. Отправка и получение одного байта 27 – 38 Сначала один байт серверу, затем из сокета читается один байт и записывается в стандартный вывод. Сеанс завершается. Перед тестированием клиента следует подготовить сервер. Сервер должен быть готов к установлению соединений с клиентами. Для этого он должен прослуживать известный ему порт с помощью системного вызова listen. Рисунок – Основные вызовы API сокетов для сервера. Но предварительно необходимо привязать адрес интерфейса и номер к прослеживающему сокету. Для этого предназначен вызов bind. #include /*UNIX*/ #include /*windows*/ int bind( socket s, const sockaddr*name, int namelen ); Возвращаемое значение:0 – нормально, -1 или SOCKET_ERROR (Windows) – ошибка Параметр s – это дескриптор просуживающего сокета. С помощью параметров name и namelen передаются порт и сетевой интерфейс, которые нужно прослеживать. Обычно в качестве адреса задается константа INADDR_ANY. Это означает, что будет прямое соединение, запрашиваемое по любому интерфейсу. Если хосту с несколькими сетевыми адресами нужно принимать соединение только по одному интерфейсу, нужно указать IP – адрес этого интерфейса. Через namelen обозначается длина структуры sockaddr_in. После привязки локального адреса к сокету нужно перевести сокет в режим прослушивания входящих соединений с помощью системного вывода listen. Его задача – пометить сокет как прислуживающий. Когда хосту поступает запрос на установление, ядро ищет для которого адрес назначения и номер порта соответствуют указанным в запросе. #include /*UNIX*/ #include /*windows*/ int listen( SOCKET s, int backlog ); 0 – нормально; -1(UNIX) или SOCKET_ERROR (Windows) – ошибка. Параметр – это дескриптор сокета, который нужно перевести в режим прослушивания. Параметр backlog – это максимальное число ожидающих, но еще не принятых соединений. Это не максимальное число одновременных соединений с данным портом, а максимальное число частично установленных соединений с данным портом, ожидающих в очереди, пока приложение их примет традиционно значение параметра backlog не более пяти соединений, ожидающих в очереди, но в современных реализациях, которые должны поддерживать приложение с высокой нагрузкой, оно может быть намного больше. Если не изучить документацию по конкретной системе и задать s больше допустимого, то системе уменьшит его, не сообщив об ошибке. Вызов accept служит для приема соединения, ожидающего во входной очереди. После того, как соединение принято, его можно использовать для передачи данных, например, с помощью вызовов recv и send. В случае успеха accept возвращает дескриптор нового сокета, по которому будет происходить обмен данными. Номер локального порта для этого сокета такой же, как и для прослушивающего сокета. Адрес интерфейса, на котором поступил запрос о соединении, называется локальным. Адрес и номер порта клиента считаются удаленными. Оба сокета имеют один и тот же номер локального порта. Это справедливо, т.к. ТСР – соединение полностью определяется четырьмя параметрами – локальным адресом, локальным портом, удаленным адресом, удаленным портом. Т.к. удаленный адрес и порт для этих двух сокетов различны, то ядро может отличить их друг от друга. #include /*UNIX*/ #include /*windows*/ int assept (socket s, structs sockaddr*addr, int*addrlen); возвращаемое значение: 0 – нормально, -1 (UNIX) или WALID_SOCKET(Windows) – ошибка. Параметр s – это дескриптор прослушивающего сокета. Как показано на рисунке 3, accept возвращает адрес приложения на другом конце соединения в структуре sockaddr_in, на которое указывает параметр addr, ядро присваивает значение, равное длине этой структуры. Часто нет необходимости знать адрес клиентского приложения, поэтому в качестве addr будет перезадаваться NULL. Приведенная ниже программа демонстрирует структуру сервера и элементарные вызовы API сокетов, которые обязан выполнить любой сервер. Простой ТСР – сервер.
- #include
- #include
- #include
- #include
- int main (void)
- struct sockaddr_in local;
- int s;
- int sl;
- int rb;
- char buf [ 1 ];
- local.sin_family = AF_INET;
- local.sin_port = htons ( 7500);
- local.sin_addr.s addr = htonl (INADDL_ANY);
- s=socket (AF_INET, SOCK_STREAM,0);
- if (s <0)
- perror («ошибка вызоваsocket»);
- exit (1);
- >
- rc = bind (s, (struct sockaddr*)
- if (rc <0)
- perror («ошибка вызоваbind»);
- exit (t);
- >
- rc = listen (s,5);
- if (rc)
- perror («ошибка вызоваlisten»);
- exit (1);
- >
- s1=assept (s,NULL, NULL);
- if (s1 <0)
- perror («ошибка вызоваassept»);
- exit (1);
- >
- rc = recv (sl, buf, 1, 0);
- if (rc <=0)
- perror («ошибка вызоваrecv»);
- exit (1);
- >
- print (“%^cn”, buf [0]);
- rc = send (s1,”2”, 1, 0);
- if (rc <=0)
- perror («ошибка вызоваsend»);
- exit (0)
- >
Источник: studfile.net
Сеть: Взаимодействие Клиент-Сервер
Этот мир — клиент-серверный, малыш. Практически всё в сети происходит по клиент-серверной логике. Возьмём хоть Telnet. При подключении к удалённому узлу на 23 порт телнетом, программа на этом хосте (так называемый telnetd, сервер telnet) как бы просыпается, возвращается к жизни. Она обрабатывает входящее telnet-соединение, обрабатывает введённые вами логин и пароль, и т.д.
В этой диаграмме показан обмен данными между клиентом и сервером.
Обратим внимание, что клиент-серверная пара может «разговаривать» через SOCK_STREAM, SOCK_DGRAM, да и как угодно иначе — до тех пор, пока они говорят «на одном языке», то есть на одинаковом протоколе.
Некоторые хорошие примеры пар клиент-сервер: telnet/telnetd, FTP/FTPd, Firefox/Apache. Каждый раз, используя фтп, на другой стороне провода вы общаетесь с FTPD-сервером.
Обычно на машине запускается только один экземпляр сервера, который обрабатывает несколько клиентов, используя fork(). Основная процедура: сервер ждёт соединения, accetp() его и fork() — рождает дочерний процесс для обработки каждого соединения. Именно так и будет работать наш простой сервер из следующего раздела.
Простой TCP-сервер
Всё, что делает этот сервер — шлёт строку «Hello, World!n» через потоковое соединение. Всё, что вам нужно сделать для проверки этого сервера — запустить его в одном окне, а в другом запустить telnet и зайти на порт своего сервера:
telnet localhost 3490
/*
** server.c — a stream socket server demo
*/ #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include #define PORT «3490» // порт, на который будут приходить соединения #define BACKLOG 10 // как много может быть ожидающих соединений void sigchld_handler ( int s )
<
while ( waitpid ( — 1 , NULL , WNOHANG ) > 0 ) ;
>
// получаем адрес сокета, ipv4 или ipv6:
void * get_in_addr ( struct sockaddr * sa )
<
if ( sa -> sa_family == AF_INET ) <
return sin_addr ) ;
>
int main ( void )
<
int sockfd , new_fd ; // слушаем на sock_fd, новые соединения — на new_fd
struct addrinfo hints , * servinfo , * p ;
struct sockaddr_storage their_addr ; // информация об адресе клиента
socklen_t sin_size ;
struct sigaction sa ;
int yes = 1 ;
char s [ INET6_ADDRSTRLEN ] ;
int rv ;
memset (
hints. ai_family = AF_UNSPEC ;
hints. ai_socktype = SOCK_STREAM ;
hints. ai_flags = AI_PASSIVE ; // use my IP
// цикл через все результаты, чтобы забиндиться на первом возможном
for ( p = servinfo ; p != NULL ; p = p -> ai_next ) <
if ( ( sockfd = socket ( p -> ai_family , p -> ai_socktype ,
p -> ai_protocol ) ) == — 1 ) <
perror ( «server: socket» ) ;
continue ;
>
if ( bind ( sockfd , p -> ai_addr , p -> ai_addrlen ) == — 1 ) <
close ( sockfd ) ;
perror ( «server: bind» ) ;
continue ;
>
if ( p == NULL ) <
fprintf ( stderr , «server: failed to bindn» ) ;
return 2 ;
>
freeaddrinfo ( servinfo ) ; // всё, что можно, с этой структурой мы сделали
if ( listen ( sockfd , BACKLOG ) == — 1 ) <
perror ( «listen» ) ;
exit ( 1 ) ;
>
sa. sa_handler = sigchld_handler ; // обрабатываем мёртвые процессы
sigemptyset (
sa. sa_flags = SA_RESTART ;
if ( sigaction ( SIGCHLD ,
perror ( «sigaction» ) ;
exit ( 1 ) ;
>
inet_ntop ( their_addr. ss_family ,
get_in_addr ( ( struct sockaddr * )
printf ( «server: got connection from %sn» , s ) ;
if ( ! fork ( ) ) < // тут начинается дочерний процесс
close ( sockfd ) ; // дочернему процессу не нужен слушающий сокет
if ( send ( new_fd , «Hello, world!» , 13 , 0 ) == — 1 )
perror ( «send» ) ;
close ( new_fd ) ;
exit ( 0 ) ;
>
close ( new_fd ) ; // а этот сокет больше не нужен родителю
>
Весь код содержится в одной большой функции main() для большей синтаксической ясности. Если вам это кажется неудобным, разбейте код на функции поменьше.
(Новая функция — sigaction() — отвечает за подчисткой зомби-процессов, которые возникают после того, как дочерний (fork()) процесс завершает работу. Если вы сделаете много зомби и не подчистите их, это не лучшим образом скажется на работе ОС).
Вы можете получить данные с сервера, используя написанный клиент, в следующем разделе.
Простой TCP-клиент
Эта штука ещё проще, чем сервер. Всё, что делает клиент — конектится к хосту, который вы укажете в командной строке, и к порту 3490. И примет строку, которую отошлёт сервер.
/*
** client.c — a stream socket client demo
*/
#define PORT «3490» // Порт, к которому подключается клиент
#define MAXDATASIZE 100 // максимальное число байт, принимаемых за один раз
// получение структуры sockaddr, IPv4 или IPv6:
void * get_in_addr ( struct sockaddr * sa )
<
if ( sa -> sa_family == AF_INET ) <
return sin_addr ) ;
>
int main ( int argc , char * argv [ ] )
<
int sockfd , numbytes ;
char buf [ MAXDATASIZE ] ;
struct addrinfo hints , * servinfo , * p ;
int rv ;
char s [ INET6_ADDRSTRLEN ] ;
if ( argc != 2 ) <
fprintf ( stderr , «usage: client hostnamen» ) ;
exit ( 1 ) ;
>
memset (
hints. ai_family = AF_UNSPEC ;
hints. ai_socktype = SOCK_STREAM ;
// Проходим через все результаты и соединяемся к первому возможному
for ( p = servinfo ; p != NULL ; p = p -> ai_next ) <
if ( ( sockfd = socket ( p -> ai_family , p -> ai_socktype ,
p -> ai_protocol ) ) == — 1 ) <
perror ( «client: socket» ) ;
continue ;
>
if ( connect ( sockfd , p -> ai_addr , p -> ai_addrlen ) == — 1 ) <
close ( sockfd ) ;
perror ( «client: connect» ) ;
continue ;
>
if ( p == NULL ) <
fprintf ( stderr , «client: failed to connectn» ) ;
return 2 ;
>
inet_ntop ( p -> ai_family , get_in_addr ( ( struct sockaddr * ) p -> ai_addr ) ,
s , sizeof s ) ;
printf ( «client: connecting to %sn» , s ) ;
freeaddrinfo ( servinfo ) ; // эта структура больше не нужна
if ( ( numbytes = recv ( sockfd , buf , MAXDATASIZE — 1 , 0 ) ) == — 1 ) <
perror ( «recv» ) ;
exit ( 1 ) ;
>
int main ( void )
<
int sockfd ;
struct addrinfo hints , * servinfo , * p ;
int rv ;
int numbytes ;
struct sockaddr_storage their_addr ;
char buf [ MAXBUFLEN ] ;
socklen_t addr_len ;
char s [ INET6_ADDRSTRLEN ] ;
memset (
hints. ai_family = AF_UNSPEC ; // если нужен только IPv4, замените на AF_INET
hints. ai_socktype = SOCK_DGRAM ;
hints. ai_flags = AI_PASSIVE ; // использовать мой IP
// цикл через все результаты, бинд на первый возможный
for ( p = servinfo ; p != NULL ; p = p -> ai_next ) <
if ( ( sockfd = socket ( p -> ai_family , p -> ai_socktype ,
p -> ai_protocol ) ) == — 1 ) <
perror ( «listener: socket» ) ;
continue ;
>
if ( bind ( sockfd , p -> ai_addr , p -> ai_addrlen ) == — 1 ) <
close ( sockfd ) ;
perror ( «listener: bind» ) ;
continue ;
>
if ( p == NULL ) <
fprintf ( stderr , «listener: failed to bind socketn» ) ;
return 2 ;
>
memset (
hints. ai_family = AF_UNSPEC ;
hints. ai_socktype = SOCK_DGRAM ;
// пробегаемся по результатам и создаём сокет
for ( p = servinfo ; p != NULL ; p = p -> ai_next ) <
if ( ( sockfd = socket ( p -> ai_family , p -> ai_socktype ,
p -> ai_protocol ) ) == — 1 ) <
perror ( «talker: socket» ) ;
continue ;
>
if ( p == NULL ) <
fprintf ( stderr , «talker: failed to bind socketn» ) ;
return 2 ;
>
if ( ( numbytes = sendto ( sockfd , argv [ 2 ] , strlen ( argv [ 2 ] ) , 0 ,
p -> ai_addr , p -> ai_addrlen ) ) == — 1 ) <
perror ( «talker: sendto» ) ;
exit ( 1 ) ;
>
printf ( «talker: sent %d bytes to %sn» , numbytes , argv [ 1 ] ) ;
close ( sockfd ) ;
Вот и всё, что необходимо сделать! Запустить «слушателя» на одной машине и «говорилку» на другой! И смотреть, как они общаются.
В принципе, вы не обязаны даже запускать сервер! Вы можете запустить только клиент, и он будет счастливо пулять пакеты в сеть, где они и сгинут, если никто на другом конце вызвать recvfrom(). Помните: UDP-сокет не даёт гарантии, что данные будут доставлены!
За исключением ещё одного случая, которую я уже не раз упоминал — UDP-сокета, устанавливающего соединение. Чтобы превратить наше приложение в устанавливающее соединение, talker должен вызывать connect() и указать адрес listener’а. с этого момента talker сможет отсылать и принимать данные только с адреса, указанного при connect(). И вместо sendto() и recvfrom() вы сможете использовать send() и recv().
Источник: masandilov.ru