Программный интерфейс сокетов
Программный интерфейс сокетов
Вы уже познакомились с интерфейсом сокетов при обсуждении реализации межпроцессного взаимодействия в BSD UNIX. Поскольку сетевая поддержка впервые была разработана именно для BSD UNIX, интерфейс сокетов и сегодня является весьма распространенным при создании сетевых приложений. В разделе "Поддержка сети в BSD UNIX" мы вновь вернемся к сокетам, когда будем рассматривать внутреннюю архитектуру сетевой подсистемы в UNIX ветви BSD. Сейчас же рассмотрим простой пример приложения клиент-сервер, который демонстрирует возможности сокетов при обеспечении взаимодействия между удаленными процессами. Несмотря на то что взаимодействие затрагивает передачу данных по сети, приведенная программа мало отличается от примера, рассмотренного в разделе "Межпроцессное взаимодействие в BSD UNIX. Сокеты" главы 3. Логика приложения сохранена — клиент отправляет серверу сообщение, сервер передает его обратно, а клиент, в свою очередь, выводит полученное сообщение на экран. Наиболее существенным отличием является коммуникационный домен сокетов — в данном случае AF_INET. Соответственно изменилась и схема адресации коммуникационного узла. Согласно схеме адресации TCP/IP, коммуникационный узел однозначно идентифицируется двумя значениями: адресом хоста (IP-адрес) и адресом процесса (адрес порта). Это отражает и структура sockaddr_in, которая является конкретным видом общей структуры адреса сокета sockaddr. Структура sockaddr_in имеет следующий вид:
struct sockaddr_in {
short sin_family; Коммуникационный домен — AF_INET
u_short sin_port; Номер порта
struct in_addr sin_addr; IP-адрес хоста
char sin_zero[8];
};
Адрес порта должен быть предварительно оговорен между клиентом и сервером.
В заключение, прежде чем перейти непосредственно к текстам программы, заметим, что интерфейс сокетов также поддерживается и в UNIX System V, наряду с другим программным интерфейсом — TLI, который будет рассмотрен в следующем разделе.
Приведенный пример в качестве транспортного протокола использует TCP. Это значит, что перед передачей прикладных данных клиент должен установить соединение с сервером. Эта схема, приведенная на рис. 6.17, несколько отличается от рассмотренной в разделе "Межпроцессное взаимодействие в BSD UNIX. Сокеты", где передача данных осуществлялась без предварительного установления связи и в данном случае соответствовала бы использованию протокола UDP.
Рис. 6.17. Схема установления связи и передачи данных между клиентом и сервером
В соответствии с этой схемой сервер производит связывание с портом, номер которого предполагается известным для клиентов bind(2), и сообщает о готовности приема запросов listen(2)). При получении запроса он с помощью функции accept(2) создает новый сокет, который и обслуживает обмен данными между клиентом и сервером. Для того чтобы сервер мог продолжать обрабатывать поступающие запросы, он порождает отдельный процесс на каждый поступивший запрос. Дочерний процесс, в свою очередь, принимает сообщения от клиента (recv(2)) и передает их обратно (send(2)).
Клиент не выполняет связывания, поскольку ему безразлично, какой адрес будет иметь его коммуникационный узел. Эту операцию выполняет система, выбирая свободный адрес порта и установленный адрес хоста. Далее клиент направляет запрос на установление соединения (connect(2)), указывая адрес сервера (IP-адрес и номер порта). После установления соединения ("тройное рукопожатие") клиент передает сообщение (send(2)), принимает от сервера ответ recv(2)) и выводит его на экран.
В программе используются несколько функций, которые не рассматривались. Эти функции значительно облегчают жизнь программисту, выполняя, например, такие действия, как трансляцию доменного имени хоста в его IP-адрес (gethostbyname(3N)), приведение в соответствие порядка следования байтов в структурах данных, который может различаться для хоста и сети (htons(3N)), а также преобразование IP-адресов и их составных частей в соответствии с привычной "человеческой" нотацией, например 127.0.0.1 (inet_ntoa(3N)). Мы не будем подробнее останавливаться на этих функциях, предоставляя читателю самостоятельно обратиться к соответствующим разделам электронного справочника man(1).
Ниже приведены тексты программ сервера и клиента.
Сервер
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <fcntl.h>
#include <netdb.h>
/* Номер порта сервера, известный клиентам */
#define PORTNUM 1500
main(argc, argv)
int argc;
char *argv[];
{
int s, ns;
int pid;
int nport;
struct sockaddr_in serv_addr, clnt_addr;
struct hostent* hp;
char buf[80], hname[80];
/* Преобразуем порядок следования байтов
к сетевому формату */
nport = PORTNUM;
nport = htons((u_short)nport);
/* Создадим сокет, использующий протокол TCP */
if ((s=socket(AF_INET, SOCK_STREAM, 0))==-1) {
perror("Ошибка вызова socket()");
exit(1);
}
/* Зададим адрес коммуникационного узла */
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv.addr.sin_port = nport;
/* Свяжем сокет с этим адресом */
if (bind(s, struct sockaddr*)&serv_addr,
sizeof(serv_addr))==-1) {
perror("Ошибка вызова bind()");
exit(1);
}
/* Выведем сообщение с указанием адреса сервера */
fprintf(stderr, "Сервер готов: %s ",
inet_ntoa(serv_addr.sin_addr));
/* Сервер готов принимать запросы
на установление соединения.
Максимальное число запросов, ожидающих обработки – 5.
Как правило, этого числа достаточно, чтобы успеть
выполнить accept(2) и породить дочерний процесс */
if (listen(s, 5)==-1) {
perror("Ошибка вызова listen()");
exit(1);
}
/* Бесконечный цикл получения запросов и их обработки */
while (1) {
int addrlen;
bzero(&clnt_addr, sizeof(clnt_addr));
addrlen = sizeof(clnt_addr);
/* Примем запрос. Новый сокет ns становится
коммуникационным узлом созданного виртуального канала */
if ((ns=accept(s, (struct sockaddr*)&clnt_addr,
&addrlen))==-1) {
perror("Ошибка вызова accept()");
exit(1);
}
/* Выведем информацию о клиенте */
fprintf(stderr, "Клиент = %s ",
inet_ntoa(clnt_addr.sin_addr));
/* Создадим процесс для работы с клиентом */
if ((pid=fork())==-1) {
perror("Ошибка вызова fork()");
exit(1);
}
if (pid==0) {
int nbytes;
int fout;
/* Дочерний процесс: этот сокет нам не нужен. Он
по-прежнему используется для получения запросов */
close(s);
/* Получим сообщение от клиента и передадим его обратно */
while ((nbytes = recv(ns, buf, sizeof(buf), 0)) !=0) {
send(ns, buf, sizeof(buf), 0);
}
close(ns);
exit(0);
}
/* Родительский процесс: этот сокет нам не нужен. Он
используется дочерним процессом для обмена данными */
close(ns);
}
}
Клиент
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <fcntl.h>
#include <netdb.h>
/* Номер порта, который обслуживается сервером */
#define PORTNUM 1500
main (argc, argv)
char *argv[];
int argc;
{
int s;
int pid;
int i, j;
struct sockaddr_in serv_addr;
struct hostent *hp;
char buf[80]="Hello, World!";
/* В качестве аргумента клиенту передается доменное имя
хоста, на котором запущен сервер. Произведем трансляцию
доменного имени в адрес */
if ((hp = gethostbyname(argv[1])) == 0) {
perror("Ошибка вызова gethostbyname()");
exit(3);
}
bzero(&serv_addr, sizeof(serv_addr));
bcopy(hp->h_addr, &serv_addr.sin_addr, hp->h_length);
serv_addr.sin_family = hp->h_addrtype;
serv_addr.sin_port = htons(PORTNUM);
/* Создадим сокет */
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Ошибка вызова socket!)");
exit(1);
}
fprintf(stderr, "Адрес клиента: %s ",
inet_ntoa(serv_addr.sin_addr));
/* Создадим виртуальный канал */
if (connect (s, (struct sockaddr*)&serv_addr,
sizeof(serv_addr)) == -1) {
perror("Ошибка вызова connect()");
exit(1);
}
/* Отправим серверу сообщение и получим его обратно */
send(s, buf, sizeof(buf), 0);
if (recv(s, buf, sizeof(buf) , 0) < 0) {
perror("Ошибка вызова recv()");
exit(1);
}
/* Выведем полученное сообщение на экран */
printf("Получено от сервера: %s ", buf);
close(s);
printf("Клиент завершил работу ");
}
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКДанный текст является ознакомительным фрагментом.
Читайте также
12.2. Программный интерфейс сигналов Linux и POSIX
12.2. Программный интерфейс сигналов Linux и POSIX 12.2.1. Посылка сигналов Посылка сигналов от одного процесса другому обычно осуществляется с помощью системного вызова kill(). Этот системный вызов подробно обсуждался в главе 10. Вариантом kill() является tkill(), который не предназначен
Программный интерфейс UNIX
Программный интерфейс UNIX Системные вызовы и функции стандартных библиотек Все версии UNIX предоставляют строго определенный ограниченный набор входов в ядро операционной системы, через которые прикладные задачи имеют возможность воспользоваться базовыми услугами,
Программный интерфейс сокетов
Программный интерфейс сокетов Вы уже познакомились с интерфейсом сокетов при обсуждении реализации межпроцессного взаимодействия в BSD UNIX. Поскольку сетевая поддержка впервые была разработана именно для BSD UNIX, интерфейс сокетов и сегодня является весьма
Программный интерфейс TLI
Программный интерфейс TLI При обсуждении реализации сетевой поддержки в BSD UNIX был рассмотрен программный интерфейс доступа к сетевым ресурсам, основанный на сокетах. В данном разделе описан интерфейс транспортного уровня (Transport Layer Interface, TLI), который обеспечивает
Программный интерфейс высокого уровня.
Программный интерфейс высокого уровня. Удаленный вызов процедур В предыдущих разделах рассматривался программный интерфейс достаточно низкого уровня — по существу программа взаимодействовала непосредственно с транспортным протоколом, самостоятельно реализуя
2.2.4 Программный интерфейс RPC
2.2.4 Программный интерфейс RPC Хотя и не так широко распространенный, как socket, программный интерфейс вызова удаленных процедур (Remote Procedure Call — RPC) для соединений типа клиент/сервер достаточно часто используется в различных системах. Первоначально он был реализован в
Глава 21 Программный интерфейс socket
Глава 21 Программный интерфейс socket 21.1 Введение Коммуникационные стандарты определяют все правила для обмена информацией в сети. Однако до некоторого момента игнорировалась необходимость стандартизации интерфейса программирования приложений (Application Programming Interface — API).
21.1.1 Программный интерфейс Berkeley
21.1.1 Программный интерфейс Berkeley К счастью, большинство реализаций TCP/IP обеспечивает программный интерфейс, следующий очень простой модели программного интерфейса socket, который впервые был предложен в 1982 г. в версии 4.1c операционной системы Unix университета Беркли (Berkeley Software
Программный комплекс «Intellectum.BIS»
Программный комплекс «Intellectum.BIS» Основное предназначение продукта – обеспечение экспертов-маркетологов и аналитиков инструментарием обработки информации для выполнения бизнес-исследований в целях обеспечения информацией руководства для принятия управленческих
Сделайте программный код красивым
Сделайте программный код красивым В общем-то, программному коду совсем не обязательно выглядеть красиво - нужно, чтобы он легко читался. В этом разделе предлагается несколько простых рекомендаций по оформлению программного кода для того, чтобы вам было легче
Программный код агента
Программный код агента Если открыть сгенерированный файл агента, вы найдете там тип, который получается из System.Web.Services.Protocols.SoapHttpClientProtocol (если, конечно, вы не указали другой протокол связи с помощью опции /protocol).public partial class CalculatorWebService : System.Web.Services.Protocols.SoapHttpClientProtocol { …}Этот
23.2. Программный сбой
23.2. Программный сбой Прежде всего, нужно выяснить и по возможности устранить причину сбоя. Если это сугубо программный сбой, то причины две: неправильная настройка программы (или системы) и ошибка
Программный DVD-проигрыватель PowerDVD
Программный DVD-проигрыватель PowerDVD Программа (рис. 5.59) воспроизводит несколько типов файлов: MPEG 1 и MPEG 2 Video, DVD, Video CD. Рис. 5.59. Интерфейс Power DVDКак и большинство DVD-проигрывателей, данная программа имеет интерфейс в виде передней панели магнитофона. В правой части панели
Программный RAID-массив
Программный RAID-массив Кроме аппаратной, существует и программная реализация RAID. В этом случае массив формируется из дисков, подключенных к обычному контроллеру SATA или IDE. Поддержка программных дисковых массивов впервые появилась в серверных операционных системах.
Программный режим
Программный режим Обозначается буквой Р. Это, в общем-то, автоматический режим, но в отличие от Авто он позволяет вам вносить свои изменения во многие выбранные камерой параметры: менять светочувствительность ISO, баланс белого, выбрать режим и точку автофокусировки,