Сокеты
Какие бы замечательные идеи в области телекоммуникаций, распределенных баз знаний или поисковых систем вам не пришли в голову, реализовать их на практике можно, лишь написав соответствующую программу. Основные операционные среды (Unix или Windows) базируются в настоящее время на идеологии сокетов (socket). Эта технология была разработана в университете г. Беркли (США) для системы Unix, поэтому соединители (сокеты) иногда называют сокетами Беркли (berkeley sockets). Сокеты реализуют механизм взаимодействия не только партнеров по телекоммуникациям, но и процессов в ЭВМ вообще (см. [2.15], а также http://book.itep.ru/7/sock_71.htm). Технология сокетов лежит в основе современного сетевого программирования.
Работа с сокетами содержит ряд этапов: сокет создается, настраивается на заданный режим работы, применяется для организации обмена и, наконец, ликвидируется. Технология сокетов поддерживает работу с любыми стеками протоколов, совмещенные процедуры ввода/вывода, использование большого числа сервис-провайдеров (серверов услуг), возможность группирования сокетов, что позволяет реализовать их приоритетное обслуживание, и многое другое. Набор операторов, поддерживающих интерфейс сервис провайдера, образует отдельную динамическую библиотеку.
Для общей синхронизации работы сервис-провайдеров и приложений в winsock введено понятие объектов событий. Объекты событий служат, в частности, для организации работы совмещенных по времени процессов информационного обмена. Здесь уместно замечание об использовании стандартных номеров портов. В многозадачных, многопользовательских системах стандартные номера портов используются при инициализации процесса. Так как допускается несколько идентичных соединений (например, несколько одновременных сессий FTP) между клиентом и сервером, стандартными номерами портов здесь не обойтись. Ведь PsIPs сервера могут соответствовать несколько PcIPc клиента.
В системах, ориентированных на соединение, пара комбинаций IP-адресов и номеров портов однозначно определяет канал связи между двумя процессами в ЭВМ. Такая комбинация называется сокетом (socket). Номера портов могут и совпадать, так как относятся к разным машинам, но IP-адреса должны быть обязательно разными. Впервые идея сокета была использована в системе BSD4.3 Unix для организации сетевого ввода/вывода. В Unix внешнее устройство и файл с точки зрения системного программиста эквивалентны. Сетевые процедуры несколько сложнее и не укладываются в такую простую схему. Из этой схемы выпадают, прежде всего, операции, при которых сервер пассивно ожидает обращения, особенно операции обмена, не ориентированные на соединение. Сокет является пограничным понятием между протоколами телекоммуникаций и операционной системой ЭВМ. Сокеты играют важную роль при написании прикладных программ (API).
Сокет отправителя = IP-адрес отправителя + номер порта отправителя
Сокет адресата = IP-адрес адресата + номер порта адресата
При обменах, ориентированных на соединение, формируется ансамбль ( IPSPS + IPDPD ), где IPSPS — адрес и порт отправителя, а IPDPD – адрес и порт места назначения.
Межкомпьютерные коммуникации не сводятся к знакомству с соседским депозитарием, к выполнению операций Telnet/ssh, FTP/scp и т.д. Одной из важнейших задач является удаленный контроль за процессами в больших распределенных системах, когда обмен информацией активизируется не человеком, а ЭВМ. Примерами таких задач могут служить управление современными высокотехнологичными производствами, сбор метео- или другой геофизической информации в реальном масштабе времени, эксперименты в области физики высоких энергий, где для контроля установки и сбора экспериментальных данных используются десятки (а иногда и сотни) вычислительных машин, которые обмениваются диагностической информацией и данными. Именно для решения таких задач и применяются идеи сокетов, "труб" и т.д.. Понятие сокета в прикладных программах — это не просто комбинация IP-адресов и номеров портов, это указатель на структуру данных, где хранятся параметры виртуального канала. Прежде чем воспользоваться сокетом, его нужно сформировать. Оператор формирования сокета имеет вид:
s=socket(INT AF, INT type, INT protocol);
где все параметры целочисленные, AF (address_family) характеризует набор протоколов, соответствующий данному сокету (это может быть набор Internet, Unix, Appletalk и т.д.). Для Интернет AF может принимать только значение PF_INET, для Unix PF_UNIX. Аргумент type определяет тип коммуникаций ( SOCK_STREAM, SOCK_RAW, и SOCK_DGRAM ). Аргумент protocol задает код конкретного протокола из указанного набора (заданного AF ), который будет реализован в данном соединении. Протоколы обозначаются символьными константами с префиксом IPPROTO_ (например, IPPROTO_TCP или IPPROTO_UDP ). Допускается значение protocol=0 (протокол не указан), в этом случае используется значение по умолчанию для данного вида соединений. Значения AF и type можно обычно найти в файле <sys/socket.h>. Возвращаемый параметр S представляет собой дескриптор сокета. Параметр SOCK_STREAM говорит о том, что вы намерены создать надежный двунаправленный канал обмена, ориентированный на соединение (TCP для Интернет). Связь с другим процессом в этом случае устанавливается оператором connect . После установления соединения данные могут посылаться оператором send или получаться посредством оператора recv. Параметр SOCK_DGRAM характеризует канал, не ориентированный на соединение, с пакетами фиксированного размера (например, UDP в случае AF= PF_INET ). Такой канал позволяет использовать операторы sendto и recvfrom. Параметр SOCK_RAW определяет третий режим, при котором возможно использование протоколов нижнего уровня, например, ICMP или даже IP. Таким образом, формирование сокета — это создание описывающей его структуры данных.
Если операция socket завершилась успешно, s равно дескриптору сокета, в противном случае s=INVALID_SOCKET (1). С помощью оператора WSAGetLastError можно получить код ошибки, проясняющий причину отрицательного результата.
Дескриптор сокета указывает на элемент таблицы дескрипторов, соответствующий данному сокету. Оператор socket отводит место в этой таблице. Элемент такой таблицы имеет вид:
код семейства протоколов;
код типа сервиса;
номер локального порта;
номер удаленного порта.
IP-адрес определяет интерфейс ЭВМ, а номер порта в данном случае характеризуют сетевую процедуру (процесс).
Эта структура данных позволяет осуществлять несколько соединений между рабочей станцией и, например, WEBсервером, в том числе имеющих разный уровень приоритета.
Так как в Unix возможно формирование сокета без IP-адресов, а для практической работы они нужны, имеется оператор bind, который позволяет присвоить определенный IP-адрес заданному сокету:
r=bind(s, const struct socketaddr far*name, int namelen),
где s — целочисленный код дескриптора, параметр name (идентификатор локального адреса) обычно (для Интернет) содержит три величины: IP-адрес ЭВМ, код протокольного набора, номер порта, который определяет характер приложения. Структура адресной информации имеет вид:
struct sockaddr { u_short sa_family; char sa_data[14]; };
Параметр namlen определяет длину второго параметра. В рамках этой идеологии легко реализовать систему клиент-сервер. IP-адрес может быть сделан равным INADDR_ANY (или =0 ), если ЭВМ имеет несколько интерфейсов. При номере порта, равном нулю, windows socket присвоит порту уникальный номер в диапазоне 1024-5000. Приложение может выполнить операцию getsockname после bind, чтобы определить присвоенный адрес. Оператор bind выполняется до операций connect или listen. При корректном выполнении оператор bind возвращает код 0 ( r=0 ), в противном случае SOCKET_ERROR=1. Команда bind выдается для записи собственного номера порта. Сервер генерирует команду bind, чтобы подготовить определенный вид связи (например, FTP), и пассивно ожидает запроса connect со стороны клиента:
R=connect(s, const struct socketaddr FAR*name, int namelen),
где s — дескриптор сокета, name — идентификатор адреса места назначения (указатель на структуру данных), а namelen — длина этого адреса. Таким образом, оператор connect сообщает IP-адрес и номер порта удаленной ЭВМ. Если адресное поле структуры name содержит нули, оператор connect вернет ошибку WSAEADDRNOTAVAIL (или SOCKET_ERROR = 1 ).
Установка в режим ожидания осуществляется командой listen, которая организует очередь запросов:
R=listen(s, int backlog),
где backlog задает максимальный размер очереди для приходящих запросов соединения (то есть, сколько запросов может быть принято на обслуживание без потерь; обычно этот параметр равен 5). При переполнении очереди будет послано сообщение об ошибке. Следует иметь в виду, что клиент, ориентированный на соединение, также должен прослушивать порт протокола, ожидая появления дейтограмм-откликов. Ожидающий сокет посылает каждому отправителю сообщение-отклик, подтверждающее получение запроса на соединение. Оператор listen подготавливает сокет к обработке потока запросов, система должна быть достаточно быстродействующей. Запросы из очереди извлекаются оператором accept :
R=accept(s, struct sockaddr FAR*addr, int FAR*addrlen),
где s — дескриптор сокета, который прослушивает соединение (тот же, что и в listen ), addr — опционный указатель на структуру, которая содержит адрес, addrlen — код длины адреса. Оператор accept позволяет серверу принять запрос от клиента. Когда входная очередь сформирована, программа реализует процедуру accept и переходит в режим ожидания запросов. Программа извлекает первый элемент очереди, создает новый сокет со свойствами, идентичными s, и при успешном выполнении возвращает дескриптор нового сокета. При возникновении ошибки возвращается код INVALID_SOCKET. По окончании обработки запроса сервер вновь вызывает accept, который возвращает ему дескриптор сокета очередного запроса, если таковой имеется. Если очередь пуста, accept блокирует программу до получения связи. Существуют серверы с параллельной и последовательной обработкой запросов. Параллельный обработчик запросов не ждет завершения обработки предшествующего запроса и вызывает оператор accept немедленно. В системе Unix используются обычно параллельные обработчики запросов.