Протоколы и алгоритмы маршрутизации в Интернет

Сокеты

Разбить на страницы
Показывать лекцию целиком

Какие бы замечательные идеи в области телекоммуникаций, распределенных баз знаний или поисковых систем вам не пришли в голову, реализовать их на практике можно, лишь написав соответствующую программу. Основные операционные среды (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-адрес;

удаленный IP-адрес;

номер локального порта;

номер удаленного порта.

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),

где accept и переходит в режим ожидания запросов. Программа извлекает первый элемент очереди, создает новый сокет со свойствами, идентичными s, и при успешном выполнении возвращает дескриптор нового сокета. При возникновении ошибки возвращается код INVALID_SOCKET. По окончании обработки запроса сервер вновь вызывает accept, который возвращает ему дескриптор сокета очередного запроса, если таковой имеется. Если очередь пуста, accept блокирует программу до получения связи. Существуют серверы с параллельной и последовательной обработкой запросов. Параллельный обработчик запросов не ждет завершения обработки предшествующего запроса и вызывает оператор accept немедленно. В системе Unix используются обычно параллельные обработчики запросов.

Возможная схема использования сокетов в случае работы N клиентов с одним WEB-сервером показана на рис. 4.1. Клиент 1 сформировал два соединения с сервером.

(рис 4.1) Формирование сокетов при подключении к одному WEBсерверу

Схема взаимодействия различных операторов winsock в рамках идеологии клиент/сервер для случая процедур, ориентированных на соединение, показана на рисунке 4.2. Горизонтальными стрелками обозначены направления посылки сетевых сообщений.

Следует иметь в виду, что программе клиента (выделена рамкой) в этом режиме не нужно знать номер порта, поэтому она не обращается к процедуре bind, а для установления связи сразу вызывает оператор connect. Современные распределенные информационные системы, WWW-серверы, поисковые системы и т.д. эффективно используют механизмы формирования сокетов и многие процедуры, описанные в данном разделе. Из литературы [2.15, 2.24] известно, что для многих видов услуг в Интернет выделены строго определенные номера портов. Доступ же к этим услугам должен быть обеспечен достаточно большому числу пользователей. С клиентской стороны при этом используются номера портов со значениями из диапазона 1024-5000. Для каждого нового клиентского запроса в ЭВМ-сервере, как правило, формируется новый процесс.

(рис 4.2) Схема взаимодействия операторов winsock для процедур, ориентированных на соединение

Лишь при успешной реализации всех перечисленных операций может начаться обмен данными. Для пересылки информации могут использоваться команды write, read, send, recv. Команды write и read имеют форму вызова:

R=write(s, buf, len) или R=read(s, buf, len),

где s — дескриптор сокета, buf — имя массива, подлежащего пересылке (или предназначенного для приема), len — длина этого массива. Оператор writev отличается от write тем, что данные могут не лежать в виде непрерывного массива:

R=writev(s, io_vect, vectlen) или R=readv(s, io_vect, vectlen),

где s — дескриптор сокета, io_vect — вектор указатель на список указателей, vectlen — длина списка указателей. Команда выполняется медленнее, чем write или read. Список указателей имеет формат (рис. 4.3):

(рис 4.3) Формат списка указателей для функций readv и writev

Команды send(s, msg_buf, buflen, flags) и recv имеют аналогичный формат, но среди параметров обращения содержат переменную flags, которая служит для целей диагностики и управления передачей данных (например, пересылка информации с высоким приоритетом ( MSG_OOB — Message Out Of Band ), что используется, в частности, при передаче звуковых сообщений). При работе с операторами send или recv надо быть уверенным, что принимающая сторона знает, что ей следует делать с этими приоритетными сообщениями. Другой возможный флаг, определяемый константой MSG_PEEK, позволяет анализировать запросы из входной очереди транспортного уровня. Обычно после считывания данных из входной очереди они уничтожаются. Когда MSG_PEEK=1, данные из входной очереди не стираются. Этот флаг используется, например, программой FTP. При успешном выполнении команды будет возвращено число переданных байтов, в противном случае —1.

Все перечисленные выше операторы рассчитаны на применение в рамках протоколов, ориентированных на установление соединения (TCP), где не требуется указание адреса места назначения. В протоколах типа UDP (не ориентированных на соединение) для передачи информации используются операторы sendto, recvfrom или sendmsg:

R=sendto(s, msg_buf, buflen, flags, adr_struc, adr_struc_len)

или recvfrom(s, msg_buf, buflen, flags, adr_struc, adr_struc_len),

где s — дескриптор сокета, msg_buf — указатель на буфер, где лежит сообщение, buflen — длина этого буфера (длина сообщения), adr_struc — адресная структура, содержащая исчерпывающую информацию об адресате, adr_struc_len — длина этой структуры. Оператор recvfrom принимает все данные, приходящие на его порт. Приняв дейтограмму, recvfrom записывает также адрес, откуда эта дейтограмма получена. Сервер может посылать по этому адресу дейтограмму­-отклик. Вызов оператора sendmsg имеет форму:

R=sendmsg(s, msg_struc, flags) [или recvmsg(s, msg_struc, flags)],

где s — дескриптор сокета, msg_struc — информационная структура, формат которой показан ниже на рисунке 4.4. Применение структур делает программирование пересылки сообщений более гибким. Следует учитывать, что для обменов, не ориентированных на соединение, сокет как бы состоит лишь из одной половины (IP-адрес и номер порта). Сокеты, созданные однажды для обмена (UDP), далее могут жить своей жизнью. Они могут принимать пакеты от других аналогичных "сокетов" и сами посылать им дейтограммы (кавычки здесь связаны с тем, что это не реальный сокет и никакого соединения здесь не осуществляется).

(рис 4.4) Формат информационной структуры msg_struc

Взаимодействие операторов winsock для систем, не ориентированных на соединение, показано на рисунке 4.5. Здесь так же, как и в случае, ориентированном на соединение, сервер вызывает socket и bind, после чего обращается к процедуре recvfrom (вместо read или recv ). Программа-клиент в данной схеме обращается к оператору bind и совсем не использует оператор connect (ведь предварительного соединения не нужно). Для передачи запросов и приема откликов здесь служат операторы sendto и recvfrom, соответственно.

Помимо уже описанных операторов для работы с сокетами имеется еще один — select позволяет процессу отслеживать состояние одного или нескольких сокетов. Для каждого сокета вызывающая программа может запросить информацию о статусе read, write или error. Форма обращения имеет вид:

(рис 4.5) Схема взаимодействия операторов winsock для процедур, не ориентированных на соединение
R=select(num_of_socks, read_socks, write_socks, error_socks, max_time),

где num_of_socks — число контролируемых сокетов (в некоторых реализациях не используется и является необязательным; по умолчанию это число не должно превышать 64).

В версии Беркли read_socks, write_socks и error_socks представляют собой побитовые маски, определяющие тип сокета. Параметр read_socks — это указатель на структуру, описывающую набор сокетов, состояние которых проверяется на возможность чтения (версия winsock ). Если сокет находится в состоянии listen, он будет помечен как "готов для чтения", при условии, что запрос на соединение уже получен. Это предполагает выполнение оператора accept без блокировки. Для других сокетов "готовность к чтению" подразумевает наличие в очереди запросов чтения. Для сокетов типа SOCK_STREAM это означает, что виртуальный сокет, соответствующий данному сокету, закрылся и операторы recv или recvfrom будут выполнены без блокировки. Если виртуальное соединение закрыто корректно, оператор recv вернет код 0, в противном случае (например, принудительное закрытие) будет возвращен код WSAECONNRESET. Параметр write_socks — указатель на набор сокетов, состояние которых проверяется на возможность записи. Если сокет находится в процессе выполнения процедуры connect, "способность к записи" означает, что установление связи завершено. Для других сокетов это значит, что операции send или sendto будут выполнены без блокировки.

Параметр error_socks — это указатель на набор сокетов, проверяемых на ошибки. В некоторых реализациях этот аргумент идентифицирует список сокетов, помеченных как приоритетные. Сокет помечается как приоритетный, если опция SO_OOBINLINE=FALSE. В случае ошибки оператор select отмечает сокет, где это произошло. Select работает лишь с теми сокетами, которые были выделены с помощью масок. При успешном выполнении оператор возвращает число сокетов, готовых к операциям ввода/вывода, и модифицирует коды масок в соответствии с состоянием сокетов. Прикладная программа может использовать результаты вызова оператора select, анализируя полученные коды масок. Аргумент max_time определяет максимальное время, выделенное select для завершения своей работы. Для уточнения типа ошибки, возникшей при исполнении операции select, можно воспользоваться процедурой WSAGetLastError.

Другим важным оператором является closesocket(s), который закрывает канал сокета с одной из сторон. Все описанные выше операторы (кроме socket, bind и listen ) блокируют работу программы до своего завершения. Практически любая операция, непосредственно связанная с выполнением процедур ввода/вывода, может блокировать выполнение других прикладных функций winsock.

Для обслуживания прикладных процессов (например, WWW-сервера, работа с распределенными базами данных и пр.) разработано много других сервисных программ (WINSOCK.DLL), перечень которых представлен в таблице 4.1.

Большинство перечисленных команд имеют развитую систему диагностики, кроме того, во многих реализациях Unix существует много других полезных команд, описание которых вы можете найти в инструкциях по использованию системы Unix. Рассмотрим некоторые из них.

Программа ioctlsocket(s, long cmd, u_long FAR*argp) служит для получения параметров сокета (выполнение не зависит от типа протокола и коммуникационной субсистемы). Аргумент cmd представляет собой код команды, которая будет выполнена для сокета s, argp — указатель на параметр команды. Возможно применение команд: FIONBIO — разрешает/запрещает режим блокировки сокета s (команда WSAAsyncSelect ставит сокет в режим запрета блокировок автоматически); FIONREAD — определяет объем данных, которые могут быть автоматически считаны через сокет s ; SIOCATMARK — задает режим чтения приоритетной информации (для сокетов типа SOCK_STREAM ).

Перечень служебных операторов для работы с сокетами (Беркли)
Имя команды назначение
getdomainname Возвращает имя домена
gethostbyname Возвращает IP-адрес для заданного сетевого имени
gethostname Возвращает имя ЭВМ (обычно имя ее домена)
gethostadr Возвращает IP-адрес ЭВМ
getnetaddr Возвращает адрес сети
getnetname Возвращает имя сети
getpeername Возвращает имя партнера, подключенного к сокету
getportbyname Возвращает имя и код протокола для указанного имени (например, ICMP, UDP или TCP)
getportbynumber Возвращает имя протокола для указанного его кода
getservbyname Извлекает из базы данных название протокола и номер порта для указанного имени сетевой услуги
getservbyport Возвращает имя сетевой услуги для заданного номера порта
getsockname Возвращает местный адрес (имя) сокета
getsockopt Запрашивает информацию о сокете
htonl Преобразует порядок байтов 32-разрядного кода из машинного в сетевой
htons Преобразует порядок байтов 16-разрядного кода из машинного в сетевой
inetaddr Преобразует символьную строку IP-адреса из десятично-точечного формата в 32-разрядный код с сетевым порядком байтов
inet ntoa Преобразует IP-адрес в десятично-точечный формат
ioctlsocket Управляет параметрами сокета, связанными с обработкой операций ввода/вывода
ntohl Преобразует порядок байтов 32-разрядного кода из сетевого в машинный
ntohs Преобразует порядок байтов 16-разрядных кодов из сетевого в машинный
ethostname Устанавливает имя ЭВМ
setsockopt Устанавливает опции сокета
shutdown Закрывает один из концов дуплексного канала для местной ЭВМ
socketpair Генерирует пару сокетов

Программа setsockopt(s, int level, int optname, const char far*optval, int optlen) устанавливает текущие значения опций для сокета s. Аргумент level описывает уровень, на котором определена данная опция (например, SOL_SOCKET или IPPROTO_TCP ); optnameимя опции, значение которой устанавливается, optval — указатель на буфер, где лежит значение опции, optlen — размер этого буфера. Для опции SO_LINGER — это размер структуры, для остальных — длина целого. При корректном исполнении setsockopt возвращает нуль, в противном случае — SOCKET_ERROR. Программа setsockopt поддерживает следующие опции ( BSD поддерживает и некоторые другие опции; колонка тип соответствует значению optval, таблица 4.2):

Опции сокетов для оператора setsockopt
опция Тип назначение
SOBROADCAST булев Позволяет передачу широковещательных сообщений
SODEBUG булев Осуществляет запись отладочных данных
SO DONTLINGER булев Разрешает закрытие без ожидания при наличии не отосланной информации. Эта опция эквивалентна SO_LINGER с l_onoff=0
SODONTROUTE булев Запрет маршрутизации — отправка непосредственно интерфейсу
SOKEEPALIVE булев Посылка сообщения keepalive ("еще жив")
SO LINGER структура Задержка закрытия в случае наличия не отосланной информации
SOOOBINLINE булев Принимает информацию, приходящую по независимым каналам, в общем потоке данных
SO RCVBUF целый Определяет размер входного буфера
SO REUSEADDR булев Позволяет сокету использовать адрес, который уже задействован
SO SNDBUF целый Определяет размер выходного буфера
TCP NODELAY булев Запрещает использование алгоритма Нагля (TCP).

Программа getsockopt(s, int level, int optname, char far*optval, int FAR* optlen) позволяет получить значение опции для любого типа сокетов. Значения параметров обращения аналогичны setsockopt. Ниже представлена таблица 4.3 поддерживаемых опций.

В среде Windows существуют аналоги (асинхронные) многих из приведенных выше операторов. Имена этих операторов имеют префикс WSA (Windows Socket Asynchronous). Асинхронными они названы по той причине, что их выполнение сопряжено с определенным диалогом и ни начало, ни завершение не ограничено какими-либо временными рамками. Список таких операторов представлен в таблицах 4.4 и 4.5 (версия windows socket 2.2).

Опции сокетов для оператора getsockopt
опция Тип назначение
SO ACCEPTCONN булев Сокет в режиме listen
SOBROADCAST булев Разрешена передача широковещательных сообщений
SO DEBUG булев Отладочный режим разрешен
SO DONTLINGER булев Если равен TRUE, SOLINGER-опция запрещена
SO DONTROUTE булев Запрет маршрутизации
SO ERROR целое Выдает статус ошибок
SOKEEPALIVE булев Сообщение keepalive ("еще жив") послано
SO LINGER структура Возвращает текущие значения опции SO_LINGER
SOOOBINLINE булев Принимает информацию, приходящую по независимым каналам, в общем потоке данных
SO RCVBUF целый Сообщает размер входного буфера
SO REUSEADDR булев Сокету разрешено использовать адрес, который был задействован
SO SNDBUF целый Сообщает размер выходного буфера
SOTYPE целый Тип сокета (например, SOCK_ STREAM )
TCP NODELAY булев Использование алгоритма Нагля запрещено (tcp)

Из этого списка можно выделить две программы WSAStartup и WSACleanup, первая вызывается в начале любой процедуры, вторая — ее завершает. WSAStartup может вызываться за время сессии несколько раз, она позволяет указать версию winsock или получить информацию о ее конкретной реализации. При вызове WSAStartup осуществляется диалог с динамической библиотекой WINSOCK.DLL и настройка параметров системы. При аварийном завершении программы нужно корректно окончить работу с WINSOCK.DLL. Следует при этом помнить, что WSACleanup воздействует на все потоки завершаемого процесса (например, в случае Windows). Определенные проблемы может вызвать перенос программ из Unix в Windows, так как там вместо read и write используются операторы recv и send, вместо ioctlioctlsocket, а вместо closeclosesocket. Некоторые операторы вообще непереносимы: readv, writv, recvmsg и sendmsg и части программы, где они содержатся, необходимо переписать. При обнаружении ошибки Unix присваивает переменной errno соответствующее значение. В winsock для этой цели используется символьная константа SOCKET_ERROR (равная 1), а для уточнения типа ошибки следует вызвать WSAGetLastError. В системе Windows этот оператор обращается к программе Win32 GetLastError, которая возвращает значение ошибки для сессии, вызвавшей эту ошибку.

Основные операторы winsock
Имя оператора назначение
WSAAsyncGetHostByAddr Аналог оператора gethostbyaddr
WSAAsyncGetHostByName Аналог оператора gethostbyname
WSAAsyncGetProtoByName Аналог оператора getprotobyname
WSAAsyncGetProtoByNumber Аналог оператора getprotobynumber
WSAAsyncGetServByName Аналог оператора getservbyname
WSAAsyncGetServByPort Аналог оператора getservbyport
WSAAsyncSelect Функциональный аналог оператора select
WSACancelAsyncRequest Прерывает выполнение операторов типа WSAAsyncgetХbyY
WSACancelBlockingCall Прерывает выполнение блокирующего оператора приложения (API)
WSACleanup Сообщает Windows sockets о завершении работы программы с DLL
WSAGetLastError Выдает сообщение о последней ошибке
WSAIsBlocking Определяет, является ли библиотека Winsock DLL блокирующей
WSASetBlockingHook Устанавливает перехват блокирующего вызова
WSASet LastError Фиксирует тип ошибки для последующего вызова WSALastError
WSAStartup Инициализирует следующий уровень Winsock
WSAUNhookBlockingHook Восстанавливает прежнюю блокировку.
Асинхронные операторы
WSAAccept* Расширенная версия accept, которая разрешает условное подключение и формирование групп сокетов
WSACloseEvent Уничтожает объект события
WSAConnect* Расширенная версия connect, которая позволяет обмениваться данными о соединении и QoS-информацией.
WSACreateEvent Создает объект события
WSADuplicateSocket Создает новый дескриптор сокета для случая использования его несколькими процессами
WSAEnumNetworkEvents Выявляет сетевые события
WSAEnumProtocols Выдает информацию о каждом доступном протоколе
WSAEventSelect Связывает сетевое событие с объектом события
WSAGetOverlappedResult Сообщает состояние выполнения совместной операции обмена
WSAGetQOSByName Выдает параметры QOS для заданного имени сетевой услуги
WSAHtonl Расширенная версия htonl
WSAHtons Расширенная версия htons
WSAIoctl* Версия ioctlsocket, пригодная для совмещения процедур ввода/вывода
WSAJoinLeaf* Подключает периферийный узел к многоточечной сессии
WSANtohl Расширенная версия ntohl
WSANtohs Расширенная версия ntohs
WSARecv* Расширенная версия recv, которая позволяет совмещать во времени операции ввода/вывода, использовать многобуферную схему при работе с векторами указателей и получать флаги в качестве входных и выходных параметров
WSARecvDisconnect Завершает работу сокета и выдает информацию о завершении, если сокет был ориентирован на работу в связанном состоянии
WSARecvErom* Расширенная версия recvfrom, которая позволяет совмещать по времени операции ввода/ вывода, использовать многобуферную схему при работе с векторами указателей и получать флаги в качестве входных и выходных параметров
WSAresetevent Обнуляет объект события
WSASend* Расширенная версия send, которая позволяет совмещать по времени операции ввода/вывода, использовать многобуферную схему при работе с векторами указателей
WSASendDisconnect Инициализирует процедуру закрытия соединения и опционно посылает сообщение disconnect
WSASendTo* Расширенная версия sendto, которая позволяет совмещать по времени операции ввода/вывода, использовать многобуферную схему при работе с векторами указателей
WSASetevent Устанавливает объект события
WSASocket Расширенная версия socket, которая использует структуру WSAPROTOCOL_INFO в качестве входной информации и позволяет создать сокеты, работающие одновременно. Позволяет также формировать группы сокетов
WSAWaitForMultiple Events Присылается, если любой или все специфицированные объекты находятся в сигнальном состоянии или когда истекает время тайм-аута
* Программа может вызвать блокировку при работе с блокирующим соке-том.

При написании программ на персональной ЭВМ, например, на С++ или ассемблере, существует возможность выбора модели памяти: small, medium, compact, large или huge. Эти модели отличаются друг от друга числом сегментов (по 64 Кбайт) памяти, выделяемой для программы и данных. Модель small предполагает, что все указатели имеют тип near, если явно не указано обратное (не записан модификатор FAR). Модель large считает по умолчанию все указатели дальними, если явно не присутствует модификатор near. В модели huge длина блока данных может превышать размер одного сегмента. С особенностями этих моделей рекомендуется ознакомиться по описаниям конкретных трансляторов. Используя те или иные модели при создании сетевых программ, нужно учитывать проблемы, которые могут возникнуть при переносе вашей программы из одной операционной среды в другую. ОС Unix и Windows не поддерживают каких­-либо моделей памяти, и их система адресации скорее совместима с моделью large. При переносе программы из одной среды в другую об этой проблеме лучше не забывать, и перед началом отладки полезно изучить особенности адресации.

Различие операционных сред особенно заметно при выполнении блокирующих процедур, рассмотренных выше, например, операции сетевого ввода/вывода. Блокирующие процедуры являются потенциальным источником "повисания" программ — скажем, оператор recv может вечно ждать отклика, в то время как удаленный сервер будет ждать сообщения от программы, выполняющей recv. По этой причине создание не блокирующих сокетов представляется привлекательным. Такие сокеты формируются путем вызова стандартного оператора socket с последующим обращением к процедуре, изменяющей режим работы сокета (по умолчанию создается блокирующий сокет). После этого при вызове блокирующего оператора, например, recv, при условии, что сокет не имеет запрашиваемых данных, система возвратит флаг ошибки. Таким образом, при работе с неблокирующим сокетом операционная система проверяет возможность немедленного выполнения процедуры и возвращает сигнал ошибки, если это невозможно. Все операторы winsock являются асинхронными и неблокирующими. Асинхронные операции при невозможности выполнить требуемую операцию не выдают сообщения об ошибке. За тем, чтобы процедура была выполнена так, как нужно, в этом случае следит операционная система. По завершении асинхронной операции ОС Windows посылает сообщение тому окну, из которого эта операция была вызвана. Но и операторы, работающие с неблокирующими сокетами Беркли, также являются неблокирующими. В то же время все операции ввода и вывода в Unix являются синхронными.

Хотя оператор WSAAsyncSelect считается аналогом select, между ними имеется существенное отличие. WSAAsyncSelect — единственный оператор, использующий дескриптор сокета в качестве параметра. Если select контролирует состояние нескольких сокетов, то для того, чтобы достичь того же результата с помощью wsaasyncselect, надо реализовать столько вызовов, сколько сокетов мы хотим мониторить. Форма обращения к WSAAsyncSelect имеет вид:

WSAAsyncSelect (SOCKETs, HWND hWnd, unsigned int wMsg, long lEvent),

где s — дескриптор сокета, состояние которого мы хотим контролировать, аргумент, hWnd — дескриптор окна получателя сообщения; wMsg определяет тип посылаемого сообщения (эти два параметра являются стандартными для всех функций Windows); lEvent — битовая маска, определяющая тип событий, которые нас интересуют. Возможные значения параметра lEvent приведены в таблице 4.6.

Возможные значения параметра lEvent оператора WSAAsyncSelect
значение назначение
FD READ Готовность к чтению
FD WRITE Готовность к записи
FD OOB Поступление Out_Of_Band данных
SD ACCEPT Контроль установки входного соединения
FD CONNECT Контроль реализованных соединений
FD CLOSE Контроль закрытия соединения

При необходимости контроля комбинации вышеперечисленных состояний эти маски могут объединяться по ИЛИ. Как только состояние сокета станет соответствовать выбранной маске, Windows пошлет прикладной программе соответствующее сообщение. Это сообщение содержит дескриптор окна, откуда осуществлен вызов процедуры WSAAsyncSelect, идентификатор сообщения, а также 16-битовый и 32-битовый параметры этого сообщения. Первый из них представляет собой дескриптор сокета, где это событие произошло. Младшие 16 бит второго параметра являются кодом события, а старшие предназначены для записи кода ошибок, если они произошли.

Как уже было отмечено, обращение к WSAAsyncSelect переводит сокет в неблокирующее состояние. При необходимости реализовать, например, процедуру recv, следует обратиться сначала к WSAAsyncSelect, запросив Windows информировать вас о готовности чтения ( lEvent=FD_READ ). После этого обработчик сообщений windows при получении соответствующего сигнала предоставит возможность прикладной программе перейти к выполнению операции recv. Так как сокет уже получил данные, блокировки не произойдет и программа их немедленно получит.

Особое внимание блокирующим операциям должно быть уделено в старых версиях Windows, поскольку блокировка остановит не только выполнение задачи, вызвавшей эту операцию, но и все другие приложения (ЭВМ станет неуправляемой на время блокировки). Здесь блокирующие процедуры должны быть запрещены, вместо этого при необходимости вызова такой процедуры windows реализует цикл проверки состояния сокета (очереди сообщений). Остальные приложения могут при этом продолжить свою работу, а при получении благоприятного сообщения Windows разрешает выполнение блокирующей процедуры, так как она уже не может вызвать блокировки. Осложнения могут возникнуть, если обработчик сообщений Windows получит сигнал, который приведет к вызову другой блокирующей процедуры. Сегодня в Windows действует правило, запрещающее такие вызовы из прикладных программ, как блокирующие, так и неблокирующие. То есть, на время выполнения блокирующей процедуры все обращения к сетевому интерфейсу запрещены.

Имеется несколько операторов Winsock, предназначенных для работы с блокирующими процедурами (смотри таблицу 4.7).

WSACancelBlockingCall прерывание блокирующей процедуры (без аргументов)
WSAIsBlocking определение блокирующей операции (без аргументов)
WSASetBlockingHook перехват блокирующего вызова, организация цикла ожидания
WSAUNhookBlockingHook восстановление прежней блокировки

При необходимости прервать блокирующую операцию можно вызвать процедуру WSACancelBlockingCall, прикладная программа получит при этом сообщение об ошибке ( WSAEINTR ). Оператор WSAIsBlocking возвращает значение TRUE, если в данный момент реализуется блокирующая операция. Последние два оператора из четырех названных служат для построения пользовательских обработчиков сообщений.

Аппарат сокетов предполагает возникновение и исчезновение вычислительных и управляющих процессов. Новый процесс может наследовать "старые" сокеты. В этом случае может возникнуть необходимость выяснить адрес партнера, с которым взаимодействует данный сокет. Эту задачу можно решить с помощью команды getpeername(s, destaddr, addrlen), где destaddr — указатель на структуру типа (рис. 4.6):

(рис 4.6) Указатель на структуру типа для команды getpeername

AF идентифицирует семейство протоколов (для TCP/IP=2 ), для которого порожден данный сокет, вся структура занимает 16 октетов. addrlen — указатель на переменную, куда будет записана длина адреса. Сокет может быть выключен командой close(s), где s — идентификатор сокета, который надлежит закрыть. Если пользователь не хочет более посылать или получать данные, он может выдать команду shutdown(s, how), где параметр how может принимать значения: 0 — блокируется прием данных; 1 — блокируется передача данных; 3 — блокируются любые обмены.

Каждое соединение должно иметь свой неповторимый код ISN (Initial Sequence Number). Этот код посылается клиентом серверу с помощью сегмента SYN. Для реализации режима соединения прикладная программа на одном конце канала устанавливается в режим пассивного доступа (passive open), а операционная система на другом конце ставится в режим активного доступа (active open). Протокол TCP предполагает реализацию 11 состояний ( ESTABLISHED, CLOSED, LISTEN, SYN_SENT, SYN_RCVD и т.д.), переход между которыми строго регламентирован (см. описание протокола ТСР выше).

При написании диагностических и управляющих программ в рамках ОС Windows можно использовать простые сокеты (Sock_Raw) или библиотеку ICMP.DLL (эта динамическая библиотека не является частью Win32 API). Библиотека ICMP.DLL содержит, в частности, процедуру ICMPSendEcho, которая посылает запросы эхо по указанному адресу и возвращает отклик в пределах указанного временного интервала. В качестве аргументов запрос ICMPSendEcho использует ICMP-дескриптор, который получается в результате запроса IcmpCreateFile.

Приложение может использовать WSAEnumProtocols для определения того, какой транспортный протокол (стек протоколов) поддерживается, и попутно можно получить дополнительную информацию, которая содержится в структуре WSAPROTOCOL_INFO.

В то время как в WinSock 1.1 имеется только одно семейство адресов AF_INET, включающее в себя ограниченное число известных типов сокетов и идентификаторов протоколов, в WinSock 2 это ограничение снято. Информация по WinSock 2 доступна по адресу:

http://www.ictp.trieste.it/~radionet/nuc1996/ref/winsock/wsspi22.htm

В настоящее время WinSock допускает совмещение по времени нескольких операций ввода/вывода. Такого рода операции возможны только для сокетов, созданных оператором WSASocket с флагом WSA_FLAG_OVERLAPPED=1 (Win32).

Запросы получения и отправки информации возвращают отклик немедленно. Если получен нуль, это означает, что операция ввода/вывода завершилась успешно; если же получен флаг SOCKET_ERROR с кодом ошибки WSA_IO_PENDING — операция начата успешно, последующие сообщения позволят судить о дальнейшем выполнении операции.

Допуская возможность нескольких операций ввода/вывода одновременно, нужно обеспечить соответствие между этими процессами и сообщениями об их завершении. В Winsock эта проблема решена с помощью введения объектов события (event objects), по аналогии с Win32. Эти объекты создаются, уничтожаются, устанавливаются в определенное состояние и т.д. Приложение может использовать оператор WSACreateEvent для создания дескриптора (указателя) объекта события, который передается в качестве обязательного параметра для совмещаемых во времени процедур посылки и получения данных ( WSASend, WSASendTo, WSARecv, WSARecvFrom ). Каждому оператору создания объекта WSACreateEvent должен соответствовать оператор WSACloseEvent, ликвидирующий его. Объекты события используются также оператором WSAEventSelect для того, чтобы связать FD_XXX сетевое события с объектами события.

В 32-разрядной среде операторы для работы с объектами события WSACreateEvent, WSACloseEvent, WSAResetEvent, WSASetEvent, WSAGetOverlappedResult и WSAWaitForMultipleEvents строго соответствуют операторам Win32.

Приложение может установить режим ожидания с блокировкой для одного или нескольких объектов события, используя оператор WSAWaitForMultipleEvents. Для этих целей можно применить и WaitForMultipleObjects. Если при ожидании предпочтительнее отсутствие блокировки, можно воспользоваться оператором WSAGetOverlappedResult, чтобы проконтролировать завершение заданного процесса.

Операторы запуска совмещаемых по времени процессов ввода/вывода WSASend, WSASendTo, WSARecv, WSARecvFrom используют в качестве опционного указателя lpCompletionRoutine, который позволяет по завершении процесса обмена передать управление определенной приложением программе.

В версии WinSock 2 введено понятие группы сокетов, которое позволяет приложению сообщить сервис-провайдеру, что данный набор сокетов имеет определенные идентичные свойства (атрибуты). К числу этих свойств относятся относительные приоритеты отдельных сокетов в пределах группы, а также спецификация качества услуг (QOS).

Приложения, реализующие мультимедийные потоки данных, нуждаются в организации специфических взаимоотношений между наборами сокетов. Как минимум это может включать подсказку сервиспровайдеру о приоритетности потоков информации. Например, при проведении видеоконференций звуковое сопровождение должно иметь более высокий приоритет, чем видеоинформация. Кроме того, существуют сервиспровайдеры, которые могут обеспечить запрашиваемое качество обслуживания (код QOS).

WSASocket и WSAAccept представляют собой два новых оператора, используемых для создания сокетов и групп, а также для включения сокета в определенную группу. Идентификатор группы сокета можно узнать с помощью оператора getsockopt с опцией SO_GROUP_ID. Установка и проверка относительного приоритета сокетов в группе осуществляется соответственно операторами getsockopt и setsockopt с опцией SO_GROUP_PRIORITY. Опции сокетов приведены в таблице 4.8.

Опции сокетов
опция Тип назначение значение по умолчанию
SO_GROUP_ ID GROUP Идентификатор группы, к которой принадлежит сокет NULL
SO_GROUP_ PRIORITY int Относительный приоритет сокетов, принадлежащих группе 0
SO_MAX_ MSG SIZE int Максимальный размер сообщения для сокетов, ориентированных на сообщения. Не имеет смысла для сокетов типа stream Зависит от реализации
SO_PROTOCOL_INFO struct WSAPROTOCOL_INFO Описание протокольной информации Зависит от протокола
PVD CONFIG char FAR * Информационная структура, содержащая данные о сервис-провайдере Зависит от реализации

Сводная таблица кодов операций для процедуры ioctl приведена ниже (таблица 4.9). Оператор WSAIoctl поддерживает также все операции, специфицированные для процедуры iocltsocket.

Коды операций
код операции входной тип выходной тип значение
SIO_ASSOCIATE_HANDLE зависит от API не использ. Связывает сокет с заданным дескриптором интерфейса-партнера
SIO_ENABLE_CIRCULAR_QUEUEING не использ. не использ. Разрешает организацию кольцевой очереди
SIO_FIND_ ROUTE struct sockaddr не использ. Запрос маршрута до заданного адреса
SIOFLUSH не использ. не использ. Аннулирует текущее содержимое очереди на отправку
SIO_GET_BROADCAST_ADDRESS не использ. struct sockaddr Определяет протокольно-зависимый широковещательный адрес для использования в sendto/ WSASendto
SIOGET_QOS не использ. QOS Определяет текущую спецификацию сокета
SIO_GET_ GROUP_QOS не использ. QOS Определяет спецификацию группы, к которой принадлежит сокет
SIO_MULTIPOINT LOO КВАСК BOOL не использ. Определяет, будут ли данные, посланные в ходе многоточечной сессии, получены сокетом локальной ЭВМ
SIO_MULTICAST_SCOPE int не использ. Определяет режим, в котором будут осуществляться мультикастинг-обмены
SIOSET_QOS QOS не использ. Устанавливает новую спецификацию для сокета
SIO_SET_ GROUP_QOS QOS не использ. Устанавливает новую спецификацию группы, к которой принадлежит сокет
SIO_TRANSLATE_HANDLE int зависит от API Возвращает дескриптор для сокета s, который соответствует контексту интерфейса-партнера

Оператор WSAAccept устанавливает условное соединение и имеет следующую структуру параметров (таблица 4.9.1).

S дескриптор сокета, который находится в режиме listen
addr опционный указатель на буфер (структуру), где должен храниться адрес подключаемого объекта. Формат адреса определяется типом протокола, заданным при создании сокета
addrlen Опционный указатель на целую переменную, которая определяет длину аргумента addr
IpfnCondition Адрес опционной условной процедуры, которая на основе полученной информации создает группу или подключает сокет к уже существующей группе
dwCallbackData Параметр, возвращаемый приложению. Этот параметр не интерпретируется WinSock

Программа извлекает очередную заявку на соединение из очереди сокета s и проверяет с помощью специфицированной программы выполнение условий соединения. Если условия выполнены, возвращается флаг CF_ACCEPT, программа создает новый сокет и осуществляет подключение его к группе в соответствии с параметром g, выработанным программой проверки условий. Вновь созданный сокет имеет такие же параметры, что и s, включая те, что задаются операторами контроля WSAAsyncSelect или WSAEventSelec t. Если программа проверки условия вернула флаг CF_REJECT, запрос на соединение аннулируется. Если невозможно принять решение немедленно, программа проверки условия должна вернуть флаг CF_DEFER, при этом никаких действий не предпринимается. Когда приложение будет готово обслужить запрос на соединение, оно снова запустит процедуру WSAAccept и пришлет либо CF_ACCEPT, либо CF_REJECT в качестве результата проверки условий.

Для сокетов, которые остаются в блокирующем режиме, когда в очереди нет запросов на соединение, WSAAccept блокирует вызывающую программу до появления заявки на соединение. Для сокетов неблокирующего типа, когда очередь пуста, оператор WSAAccept вернет флаг ошибки.

При завершении процедуры в addrlen будет записана реальная длина адреса в байтах. Если addr и (или) addrlen равны нулю, это означает, что нет никакой информации об адресе удаленного адресата. В противном случае эти параметры несут в себе реальную информацию независимо от результатов проверки условий.

  • Современные версии WinSock обеспечивают протокольно­-независимый доступ ко всем ресурсам сети, включая такие стандартные приложения, как DNS, SAP, X.500 и т.д.
  • Они позволяют реализовать несколько сессий ввода/вывода одновременно.
  • Они поддерживают любые стандартные уровни сервиса (QOS — Quality of service), осуществляют объединение сокетов по группам в соответствии с их параметрами.
  • Допускается многопротокольная широковещательная или мультикастинг­-адресация.
  • Предусматривается совместное использование сокетов несколькими процессами, условное создание сокетов и объединение их в группы.
  • Введено понятие WOSA-интерфейса (Windows Open Service Architecture), который обеспечивает связь между прикладной программой и сетевыми процедурами ОС. Этот интерфейс заметно упрощает программирование при работе с пакетами.

    Каждая процедура, распознаваемая WOSA, имеет, в свою очередь, несколько интерфейсов, ориентированных на конкретных сервис-провайдеров. Применение этих средств реализуется через DLL.

    WinSock, следуя модели WOSA, обеспечивает прикладной интерфейс для сетевого программирования API (Application Programming Interface), который организует доступ к транспортным услугам и серверу имен сервис-провайдера SPI (Service Provider Interface). SPI ориентирован на использование в рамках 32-битовой модели Microsoft Windows.

    Транспортные сервис-провайдеры (например, TCP/IP или IPX/SPX) и доменные серверы (DNS) в WinSock представляют собой динамические библиотеки (DLL) с одной точкой входа для процедур инициализации WSPStartup или NSPStartup (обратите внимание на то, что здесь под сервис-провайдером подразумевается система, способная предоставить определенный вид услуг). Все остальные процедуры сервис-провайдера сделаны доступными для WinSock DLL через диспетчерскую таблицу. Динамическая библиотека сервис-провайдера загружается в память WinSock DLL только по мере необходимости и выгружается, когда она более не нужна. Динамическая библиотека сервис-провайдера имеет обычно расширение .WSP или .NSP. Процедуры WinSock SPI имеют префиксы (таблица 4.9.2.).

    Сервис-­провайдеры WinSock при работе со строками применяют UNICODE. WinSock DLL выполняют необходимые преобразования при работе с приложениями, использующими ANSI или UNICODE.

    WSP WinSock Service Provider для транспортных услуг сервис-провайдера
    WPU WinSock Provider Upcall для входа в WinSock DLL сервис-провайдера
    WSC WinSock Configuration для входа в WinSock DLL инсталляционных приложений
    NSP Name Space Provider для работы с сервером имен

    Конкретный сервис-провайдер может поддерживать один или более протоколов. Так, TCP/IP-провайдер должен как минимум поддерживать TCP и UDP-протоколы. Каждый поддерживаемый протокол описывается в структуре WSAPROTOCOL_INFO, а набор таких структур представляет собой каталог используемых протоколов (более детальную информацию по данной тематике можно найти по адресу: http://www.ictp.trieste.it/~radionet/nuc1996/ref/winsock/wsspi22.doc).

    Одной из главных задач WinSock DLL является выполнение функции регулировщика информационных потоков между приложениями и сервиспровайдерами. Каждый сервиспровайдер взаимодействует только с WinSock DLL. WinSock DLL заботится об объединении потоков событий от разных сервиспровайдеров и направлении их приложению. Эта библиотека берет на себя функции арбитража и синхронизации. Взаимоотношения между сервиспровайдерами (даже если они поддерживают разные протоколы) улаживаются также WinSock DLL.

    Работа WinSock DLL базируется на параметрах сокетов, которые задаются при формировании ( socket и WSASocket ), именно эта информация определяет, какой из сервис-провайдеров будет задействован. Для выбора сервис-провайдера применяется процедура WSPSocket. В случае процедуры socket, WinSock DLL находит запись в структуре WSAPROTOCOL_INFO, которая соответствует входным параметрам (идентификатор стека протоколов, тип сокета, протокол).

    Сервис­-провайдеры используют сетевые протоколы низкого уровня. WinSock DLL осуществляет управление на среднем уровне, обеспечивая связь транспортных протоколов с приложениями.

    Транспортный SPI WinSock аналогичен WinSock API с точки зрения базовых процедур с сокетами. Так, процедурам connect и WSAConnect ставится в соответствие WSPConnect ; accept и WSAAcceptWSPAccept, а socket и WSASocketWSPSocket.

    Так как WinSock DLL не поставляется теперь разработчиками стека протоколов, расширение функциональных возможностей представляет определенную проблему. Для преодоления этих трудностей новейшие версии WinSock используют процедуру WSAIoctl, чтобы помочь сервис-провайдерам на практике реализовать какие-­либо функциональные новшества.

    Для введения расширенных возможностей приложение должно запросить указатель на точку входа в соответствующую программу. Это делается с помощью процедуры WSAIoctl, используя команду SIO_GET_EXTENSION_FUNCTION_ POINTER. При этом входной буфер процедуры WSAIoctl содержит идентификатор запрашиваемой функции, в выходной буфер будет занесен указатель на нужную программу.

    Идентификаторы, присвоенные новым функциям, должны быть уникальными глобальными идентификаторами GUID (Global Unique Identifiers), которые присваиваются им поставщиками сервис-провайдеров.

    Для того, чтобы транспортный протокол стал доступен через WinSock, он должен быть правильно инсталлирован и зарегистрирован в WinSock. Когда транспортный сервис-провайдер инсталлирован с помощью программы поставщика, соответствующая информация должна быть введена в конфигурационную базу данных, чтобы дать WinSock DLL необходимые данные для взаимодействия с сервис-провайдером.

    В WinSock DLL содержится процедура WSCInstallProvider для инсталляции программ поставщиков и процедура выгрузки этих программ WSCDeinstallProvider.

    Структура WSAPROTOCOL_INFO поставляется для каждого протокола и указывает на то, является ли этот протокол базовым, слоевым или стеком. Величина поля ProtocolChain.ChainLen интерпретируется таким образом (таблица 4.9.3.).

    0 протокол слоя базовый протокол (или стек, если стек состоит из одного протокола)
    >1 стек протоколов.

    Инсталляция стека протоколов возможна лишь после загрузки всех составных частей (слоевых и базовых протоколов). Структура WSAPROTOCOL_INFO для стека протоколов использует поле ProtocolChain для описания длины стека и идентификации каждой из составных частей. Отдельные протоколы, входящие в стек, последовательно перечислены в массиве ProtocolChain.ChainEntries, нулевой элемент списка соответствует первому протоколу слоя.

    SDK (System Development Kit) для WinSock включает в себя инструментальную WinSock и отладочную DLL.

    При разработке протокольно-­независимых приложений для систем клиент-сервер нужно зарегистрировать имя сервера в банке имен: только это может сделать его доступным извне. Программа-клиент может функционировать лишь при условии, если она способна найти необходимую ей процедуру в поле имен и получить доступ к соответствующему транспортному протоколу и адресной информации. Для тех, кто работает с протоколами TCP/IP, это может в начале вызвать определенные трудности, которые компенсируются возможностями создания программ, пригодных для широкого класса самых разнообразных протоколов. Под работой с полем имен здесь подразумевается возможность установления соответствия между протоколом и адресным атрибутом сетевой услуги, которой присвоено какое-­то имя. Примерами таких полей имен могут служить уже используемые системы DNS (Domain Name System), NDS (Netware Directory Services), X.500 и др. Поля имен могут быть динамическими, статическими и постоянными.

    Динамические поля имен служат для краткосрочной регистрации сетевой услуги. Часто они базируются на широковещательной системе определения доступности той или иной услуги. Примерами таких систем являются SAP в среде Netware и NBP в среде Appletalk.

    Статические поля имен формируются при первоначальном создании сервера имен и в дальнейшем могут изменяться лишь администратором сети. Традиционная система DNS относится именно к этой разновидности. Программа может пользоваться таким банком имен, но не может произвести туда запись.

    Постоянное поле имен сходно с динамическим — запись туда происходит в реальном масштабе времени, но после регистрации имя записывается в постоянную память и остается там до тех пор, пока не придет запрос на ликвидацию этой записи. Примерами такой системы могут служить X.500 и NDS.

    Некоторые поля имен организованы иерархически. X.500 и NDS, например, позволяют неограниченное число уровней вложения. Программные интерфейсы, предназначенные для посылки запросов в именные базы данных, сильно варьируются в зависимости от разновидности используемого поля имен. Сервер имен представляет собой резидентную программу, которая обеспечивает интерфейс между WinSock SPI и некоторой существующей базой данных имен.

    Используемые услуги в WinSock группируются в классы услуг, и в пределах класса имя услуги должно быть уникальным.

    Примерами классов услуг могут служить FTP и SQL. Каждому классу присваивается имя и идентификатор (ID). Имя класса может не быть уникальным, но идентификатор обязан быть неповторимым. В качестве идентификаторов классов услуг в Winsock применяются GUID (Globally Unique Identifiers). Для генерации GUID имеется специальная программа (UUIDGEN.EXE), которой может воспользоваться разработчик новых классов услуг.

    DNS в Internet не имеет развитой системы для записи информации о классах услуг. Для TCP/IP-класса услуг GUID присвоен раз и навсегда.

    WinSock DLL может маршрутизовать прикладные операции в пространстве имен и переадресовывать их соответствующим серверам имен.

    Процедуры установки классов услуг, регистрации и обслуживания запросов осуществляются непосредственно через интерфейс APISPI. Процедура WSAGetServiceClassNameByServiceClassId не имеет аналога среди операторов SPI, так как эта процедура WinSock DLL осуществляет обращение к NSPGetServiceClassInfo.

    Переключение сервера имен из активного состояния в пассивное и обратно осуществляется с помощью процедуры WSCEnableNSProvider. В активном состоянии могут находиться несколько серверов имен одновременно. Инициализация сервис-провайдеров выполняется с помощью процедуры WSPStartup (см. аналогично WSAStartup ). Каждой процедуре WSPStartup должна соответствовать процедура WSPCleanup.

    Процедура WSPSocket имеет параметр flags, который позволяет контролировать атрибуты сокета. Флаг WSA_FLAG_OVERLAPPED указывает на то, что данный сокет будет использоваться в режиме совмещения процессов ввода/вывода.

    Существуют атрибуты, ответственные за использование сокета для работы в многоточечном и/или мультикастинг режимах. Только сокеты, созданные для работы в многоточечном режиме, пригодны для запуска многоточечных сессий с помощью WSPJoinLeaf.

    Сокет, созданный для работы в блокирующем режиме, может быть преобразован в неблокирующий с помощью процедур WSPAsyncSelect, WSPEventSelect или WSPIoctl. Перевод сокета в блокирующий режим производится посредством процедур WSPIoctl, если WSPAsyncSelect неактивен, или WSPEventSelect — если активен.

    Процедура WSPCloseSocket ликвидирует дескриптор сокета, и все процессы, использующие этот сокет, будут прерваны.

    Понятие блокировки для среды Windows было фундаментальным. В WinSock 1.1, блокирующие процедуры WinSock были проблемой, так как они парализовали взаимодействие приложения и надстройки Windows, а применение псевдо-блокирующей техники не всегда давало удовлетворительный эффект (вспомните "голубой экран смерти" в Windows95!). В ОС типа Windows NT блокирующие процедуры уже не могут вызвать каких-­либо проблем, более того, они уже представляются привлекательными. Интерфейс WinSock 2 API не поддерживает более псевдо-блокировку, но для обеспечения совместимости с WinSock 1.1 он эмулирует этот механизм.

    В среде Win16, где настоящее блокирование не поддерживается ОС, блокирующие процедуры, которые не могут быть закончены немедленно, обслуживаются с использованием псевдо-блокировки. Сервис­-провайдер инициализирует процедуру, после чего входит в цикл, внутри которого осуществляет доставку любых сообщений Windows и проверяет завершение процедуры. Если процедура завершилась или если вызван оператор WSPCancelBlockingCall, происходит выход из цикла, а блокирующая процедура завершается с соответствующим результатом.

    Эта схема вполне приемлема для простых приложений. Но она неприемлема для приложений, где должны реализоваться сложные схемы доставки сообщений, например, для модели MDI (Multiple Document Interface). Для таких приложений псевдо-блокировка должна быть реализована в самом приложении. Сервис­-провайдер в этом случае должен вызывать именно этот цикл. Для этого он должен получить указатель на него, обратившись к WPUQueryBlockingCallback.

    Сервис-­провайдер WinSock не может исходить из предположения, что псевдо-блокировка, используемая приложением, обеспечит обработку сообщений. Если приложение не приспособлено для решения таких задач, оно откликнется флагом FALSE. Если сервис-провайдер требует обработки сообщений для своих внутренних нужд, он может вызвать процедуру PeekMessage до передачи управления циклу псевдо-блокировки в приложении.

    Сервис­-провайдер WinSock использует псевдо-блокировку лишь при выполнении определенных условий:

  • программа определена как блокирующая;
  • соответствующий сокет работает в блокирующем режиме;
  • запрос не может быть выполнен немедленно.
  • Если условия не выполнены, уход в цикл псевдоблокировки осуществлен не будет.

    Если во время цикла псевдо-блокировки получено сообщение Windows, существует опасность, что будет предпринята попытка вызова еще одной процедуры WinSock. Из­-за трудностей управления этим процессом WinSock 1.1 запрещает такие вызовы. Любая попытка осуществить вложенный вызов процедур WinSock вызовет ошибку WSAEINPROGRESS. В WinSock 1.1 это ограничение справедливо как для блокирующих, так и для неблокирующих процедур. Имеется два исключения из этого правила: процедура, которая позволяет приложению проверить, находится ли система в цикле псевдо-блокировки ( WSAIsBlocking ), и процедура ухода из этого цикла ( WSPCancelBlockingCall ).

    WinSock 2 DLL имеет средства для формирования объектов событий, как для приложения, так и для сервис-провайдера, хотя на практике объекты событий создаются в основном приложениями. Для формирования объектов событий в WinSock имеется оператор WPUCreateEvent. Дескриптор объекта события имеет смысл лишь в контексте программы, его сформировавшей. В среде Win32 работа с объектами событий выполняется самой операционной системой.

    Объекты событий в WinSock представляют собой простые конструкции, которые могут создаваться и уничтожаться, они могут устанавливаться и сбрасываться. Клиент создает объект события и передает его дескриптор в качестве параметра таким процедурам как WSPSend и WSPEventSelect. Когда оговоренные условия выполнены, сервиспровайдер использует дескриптор для того, чтобы установить объект события с помощью оператора WPUSetEvent. При этом клиент WinSock SPI может находиться в состоянии блокировки или в режиме запроса, ожидая, когда объект события будет установлен. Клиент может сбросить объект события в нуль, снова его установить и снова использовать.

    Субъект (приложение или сервиспровайдер), создавший объект события, ответственен и за его ликвидацию. Сервис­провайдер может это сделать с помощью WPUCloseEvent.

    Одной из главных задач сервиспровайдера является сообщение приложению о том, что произошло соответствующее сетевое событие. Список сетевых событий выглядит таким образом (таблица 4.9.4.).

    FD_CONNECT Канал до удаленной ЭВМ или для мультикастинг-сессии сформирован
    FD_ACCEPT Удаленная ЭВМ выставила запрос на соединение
    FD_READ Получены данные и их можно считать
    FD_WRITE В буферах сервис-провайдера появилось свободное место и можно послать очередную порцию информации
    FD_OOB Для чтения доступна высокоприоритетная информация
    FD_CLOSE Удаленная ЭВМ закрывает канал
    FD_QOS Произошло изменение оговоренного значения QOS (качества услуг)
    FD_GROOUP_QOS Произошло изменение оговоренного значения QOS для данной группы сокетов

    Стандартный BSD-интерфейс сокетов имеет только одно средство получить информацию о сетевых событиях — оператор select. Этот метод не может дать информацию о событиях FD_QOS и FD_GROUP_QOS.

    В Windows Sockets 1.1 используется асинхронный механизм получения информации о сетевых событиях. Для регистрации интересующих событий можно использовать процедуру WSPAsyncSelect. Когда нужное сетевое событие произойдет, соответствующему окну будет послано сообщение, заданное клиентом. Сервис­-провайдер использует для тех же целей процедуру WPUPostMessage. В среде Win32 этот метод получения данных о событиях нельзя считать эффективным.

    WSPEventSelect ведет себя практически так же, как WSPAsyncSelect, за исключением того, что вместо посылки сообщения Windows при сетевом событии типа FD_XXX устанавливается соответствующий объект события.

    Факт прихода сетевого события FD_XXX сервис-провайдером запоминается. Вызов процедуры WSPEnumNetworkEvents вызывает копирование текущего содержимого памяти сетевых событий в буфер клиента, а основная память событий очищается.

    Сервис­-провайдеры (ISDN или ATM) могут использовать групповые значения QOS при формировании виртуального канала и повышения эффективности своей работы. Пакеты для сокетов в пределах группы мультиплексируются обычным образом.

    Операторы WSPSocket и WSPAccept предназначены для формирования и подключения новых сокетов к той или иной группе. Однажды включенный в группу сокет остается в ней до своего закрытия. Группа прекращает свое существование, лишь когда последний сокет группы будет закрыт. Идентификатор группы сокетов может быть получен с помощью оператора WSPGetSockOpt с опцией SO_GROUP_ID. Узнать об относительном приоритете сокета в группе можно, воспользовавшись процедурой WSPGet/SetSockOpt с опцией SO_GROUP_PRIORITY.

    Групповое значение QOS можно задать при выполнении WSPConnect, или WSPIoctl с SIO_SET_GROUP_QOS, если специфицированный сокет является учредителем группы. Оператор WSPIoctl с SIO_GET_GROUP_QOS может использоваться для получения группового значения QOS заданного сокета.

    Для провайдеров, поддерживающих группирование сокетов, минимально необходимы следующие функции:

  • создание новых групп и присвоение им уникальных идентификаторов;
  • возможность включения сокета в одну из существующих групп;
  • учет провайдером значения QOS порождающего сокета при формировании списка параметров нового сокета, включаемого в группу.
  • Существуют группы двух видов. Одни включают в себя сокеты, ориентированные на соединение, они могут быть подключены только к определенному адресу ЭВМ, остальные группы относятся к другому виду. Провайдер не может включить в группу сокет, который не удовлетворяет этому условию. Провайдер не может также выполнить соединение, если адресат не соответствует адресу места назначения группы сокетов. Групповой адрес места назначения определяется, когда выполняется процедура connect для первого из сокетов группы.

    Единственный индивидуальный параметр сокета в группе — его внутренний уровень приоритета.

    Провайдер должен уметь считывать значение приоритета, но он может полностью игнорировать этот параметр.

    В настоящее время не существует каких-­либо механизмов для сопоставления приоритетов сокетов, принадлежащих к разным группам, или сокетов вне групп.

    Поддержка провайдеров групп не означает непременную поддержку различного качества услуг (QOS, см. RFC-1363).

    Поддержание работы с приоритетами сокетов в пределах группы является рекомендательным требованием. Схема приоритетов WinSock 2 имеет смысл лишь для мультиплексирования данных при соединении точка­-точка, например, в случае телефонной связи или любых других вариантов, реализующих коммутацию каналов.

    Характер информационного обмена описывается спецификацией потока (flow specs), для каждого сокета используется две такие спецификации, по одной для каждого из направлений обмена. Запрос на соединение реализуется через процедуры WSPConnect или WSPIoctl с кодом команды SIO_SET_QOS/SIO_SET_GROUP_QOS. Спецификация потока параметрически задает уровень сервиса (QOS) и определяет механизм адаптации приложения к сетевым условиям. В WinSock 2 спецификация потока содержит следующие характеристики QOS.

  • Описание источника информационного потока, которое характеризует способ, используемый приложением для введения потока данных в сеть. Эта спецификация выполняется в терминах модели маркерного ведра (token bucket) и включает частоту генерации маркеров, их размер, а также максимальное значение информационного потока.
  • Задержка (latency) — приемлемые верхние пределы задержки и ее вариации.
  • Гарантированный уровень обслуживания. Предполагается, что провайдер, который не способен обеспечить требуемый уровень сервиса (QOS), не сможет быть подключен.
  • Цена (параметр зарезервирован на будущее).
  • Параметры, специфические для провайдера (спецификация потока может быть расширена, чтобы имелась возможность описать некоторые особенности провайдера).
  • Для протоколов, ориентированных на соединение, более удобно для приложения оговаривать QOS при формировании соединения. Это делается путем запроса WSPConnect, направленного сервис-провайдеру. Если QOS было задано с помощью WSPIoctl, его значение может быть переписано при выполнении процедуры WSPConnect.

    Бессвязные сокеты могут также использовать WSPConnect с целью установления определенного уровня QOS для канала связи с партнером

    После каждой попытки установить связь провайдер производит коррекцию спецификации потока для того, чтобы наилучшим образом отразить реальную ситуацию в сети. Предполагается, что клиенты могут использовать текущую сетевую информацию, чтобы оптимально реализовать возможности сети.

    Но после того как информация о состоянии сети получена, условия могут измениться, партнеры могут согласовать другой уровень QOS, так что приложение должно быть готово ко всему. Для информирования клиента о возможных изменениях условий в Winsock используется механизм сетевых событий ( FD_QOS и FD_GROUP_QOS ). Сервис-­провайдер должен генерировать события типа FD_QOS/FD_GROUP_QOS, если уровень сервиса изменился значительно. Клиент должен использовать WSPIoctl с кодами команд SIO_GET_QOS и/или SIO_GET_GROUP_QOS, чтобы получить соответствующую спецификацию потока и выяснить, изменился ли уровень сервиса (QOS). Структура QOS должна актуализоваться вне зависимости от типа события FD_QOS/FD_GROUP_QOS. Если новый уровень сервиса неприемлем, клиент может попытаться приспособиться к новым условиям или закрыть соединение. При повторной попытке согласовать уровень QOS успешный выход из процедуры WSPIoctl указывает, что новое значение QOS приемлемо. Структура QOS в WinSock 2 описана в файле Winsock2.h.

    Приложения, которые не склонны иметь дело с QOS-структурой непосредственно, могут воспользоваться именами известных видов сервиса и получить нужные данные с помощью запроса WSPGetQOSByName.

    Прежде чем использовать сокет, надо связать его с локальным адресом. Это выполняется с помощью процедуры WSPBind, или WSPConnect.

    Сервер сначала создает сокет, связывает его с известным локальным адресом (что позволяет клиенту найти его) и переводит сокет в режим ожидания посредством WSPListen, готовя его к приему запросов на соединение. Одновременно система подготавливает структуру для формирования очереди запросов. Сервис-­провайдер заносит поступающие запросы в очередь, где они ожидают обработки. Запросы могут быть удалены клиентом из очереди по таймауту.

    Если использован сокет блокирующего типа, сервер может немедленно запустить процедуру WSPAccept, которая вызовет блокировку на время ожидания запроса на соединение. В качестве альтернативы сервер может использовать один из механизмов обработки сетевых событий, описанный ранее. В зависимости от выбранного механизма, провайдер или пошлет сообщение Windows, или даст знать о приходе запроса на соединение через объект события.

    Клиент со своей стороны создаст соответствующий сокет, запустит процедуру подключения и пошлет запрос WSPConnect, указав известный адрес сервера. Если сокет является блокирующим, WSPConnect вызовет блокировку до тех пор, пока сервер не получит и не обработает запрос на соединение или пока не произойдет таймаут. Клиент должен использовать механизм сетевых событий для отслеживания процесса соединения.

    Когда сервер запускает процедуру WSPAccept, сервис-провайдер инициализирует программу проверки условий, поставляемую приложением. В качестве параметров обращения используется информация, полученная вместе с запросом на соединение (берется из очереди запросов). Эта информация включат в себя адрес подключаемой ЭВМ, данные о пользователе и характеристику QOS, если таковая имеется. На основе полученной информации сервер определяет, принять данный запрос или нет.

    Если сервер принял запрос, он должен сформировать новый сокет с теми же атрибутами, что и сокет, ожидавший прихода запроса. Исходный сокет остается в режиме ожидания последующих запросов.

    Для получения локального адреса сопряженными сокетами используется запрос WSPGetSockName. Это особенно полезно, когда послан запрос WSPConnect без предварительного вызова процедуры WSPBind.

    Как было сказано ранее, процедура WSPAccept позволяет воспользоваться поставляемой клиентом программой проверки условий, которая осуществляет условную обработку запросов на соединения, ожидающих обслуживания. Принятие решения выполняется на основе информации, поступающей вместе с запросом (идентификатор источника запроса, QOS и т.д.). Если программа проверки условий возвращает флаг CF_ACCEPT, формируется новый сокет с теми же свойствами, что и исходный. Если программа проверки вернет флаг CF_REJECT, запрос на соединение будет отвергнут. При возвращении флага CF_DEFER принятие решения откладывается, а запрос на соединение остается в очереди. Клиент должен будет осуществить вызов WSPAccept еще раз.

    Некоторые протоколы позволяют переслать информацию в процессе установления связи. Если такая информация получена, она помещается в буфер провайдера, а WinSock SPI получает указатель на этот буфер и длину записи. Если клиент WinSock SPI желает послать некоторую информацию в ответ, он может скопировать ее в буфер, предоставляемый сервис-провайдером.

    Разрыв соединения может быть выполнен несколькими способами. Для инициализации прерывания связи можно, например, применить процедуру WSPShutdown (с параметром how равным SD_SEND или SD_BOTH ), и WSPSendDisconnect. Процедура WSPCloseSocket может быть применена и для аварийного прерывания соединения.

    Сервис-­провайдер сообщает о разрыве соединения по инициативе удаленного партнера с помощью сетевого события FD_CLOSE. При реализации процедуры разрыва соединения возможен обмен определенной информацией, если это предусмотрено используемым протоколом.

    Инициатор разрыва связи может вызвать WSPSendDisconnect, чтобы сообщить, что данных больше посылаться не будет и можно ликвидировать соединение. При этом может быть послана некоторая служебная информация, предусмотренная протоколом обмена. Для чтения этой информации партнер может воспользоваться оператором WSPRecvDisconnect. По завершении этого обмена обе стороны вызывают процедуры WSPCloseSocket.

    Следует различать процедуры прерывания ( shutdown ) связи и ее разрыва ( close ). Прерывание связи сопряжено с определенным диалогом между партнерами, после чего связь "замораживается", но дескриптор связи сохраняется. Существует два типа прерывания связи: аварийное (аппаратное) и нормальное. При нормальном прерывании любые данные, которые стояли в очереди, пересылаются до завершения процесса прерывания. При аварийном же прерывании непосланная информация теряется.

    В Windows как процедура WSPShutdown, так и WSPSendDisconnect могут использоваться для инициализации прерывания связи, в то время как запрос WSPCloseSocket служит для ликвидации дескрипторов сокетов и освобождения связанных с ними ресурсов.

    Путем установки определенных значений для опций сокетов SO_LINGER и SO_DONTLINGER можно получит следующие варианты реализации процедуры WSPCloseSocket.

  • Процедура аварийного прерывания соединения, которая возвращается из WSPCloseSocket немедленно.
  • Нормальное прерывание соединения (graceful shutdown); задержка исполнения до завершения исполнения процедуры или до истечения заданного времени. Если таймаут наступает до завершения процедуры прерывания соединения, запускается процесс аварийного разрыва соединения.
  • Нормальное прерывание соединения с немедленным возвратом и завершением процедуры прерывания в фоновом режиме. Этот алгоритм реализуется по умолчанию. Приложение при этом не знает, когда и как завершается процесс прерывания соединения.
  • Для сокетов, которые соответствуют протоколам, не ориентированным на соединение, работа, выполняемая оператором WSPConnect, связана главным образом с установлением адреса места назначения по умолчанию, что позволит в дальнейшем использовать сокет в операциях обмена, ориентированных на соединение ( WSPSend и WSPRecv ). Любые дейтограммы, полученные от отправителя с адресом, отличным от специфицированного, будут проигнорированы.

    Место назначения по умолчанию может быть изменено с помощью повторного вызова WSPConnect. Любая дейтограмма из входной очереди будет отброшена, если новый адрес отличается от адреса, заданного в предыдущем WSPConnect.

    Если новый адрес равен нулю, сокет будет отсоединен, так как удаленный адрес не определен, в результате операторы WSPSend и WSPRecv возвратят флаг ошибки WSAENOTCONN ; в то же время WSPSendTo и WSPRecvFrom могут использоваться по-прежнему. Существует три базовых способа выполнения операций ввода/вывода:

  • блокирующий ввод/вывод;
  • неблокирующий ввод/вывод с асинхронными сетевыми событиями;
  • совмещенный ввод/вывод.
  • Первый вариант является режимом по умолчанию, неблокирующий вариант может использоваться для любого сокета, который поставлен в соответствующий режим. Третья разновидность обмена реализуется только для сокетов, которые при формировании были объявлены совмещенными. Процедуры посылки WSPSend и WSPSendTo и приема WSPRecv и WSPRecvFrom реализуют все три указанных режима обмена. Сервис-­провайдеры определяют метод обмена на основе режима работы сокета, атрибутов и входных параметров.

    Простейшим режимом обмена в WinSock 2 является блокирующий ввод/вывод. Этот режим устанавливается по умолчанию. Любая операция ввода/вывода на блокирующем сокете вернет управление системе только по завершении процедуры. Таким образом, в ходе любой сессии может выполняться только одна операция ввода/вывода. Это простой режим, но отнюдь не самый эффективный.

    Если сокет находится в неблокирующем состоянии, любая операция обмена должна либо завершаться немедленно, либо возвращать флаг ошибки WSAEWOULDBLOCK, указывая, что операция не может быть завершена корректно. Необходим механизм для определения момента, когда следует попытаться выполнить операцию еще раз. Для решения этой проблемы определен список сетевых событий, наступление которых может контролироваться с помощью процедур WSPSelect, WSPAsyncSelect или WSPEventSelect.

    В WinSock 2 впервые разрешено совмещение нескольких процедур ввода/вывода и затребована поддержка этого режима всеми сервис-провайдерами. Этот режим возможен только для сокетов, сформированных с помощью WSPSocket с флагом WSA_FLAG_OVERLAPPED.

    Для приема данных клиент может воспользоваться процедурами WSPRecv или WSPRecvFrom, чтобы указать буферы, куда будут записываться данные. Если один или более буферов подготовлен приложением до начала обмена, информация будет заноситься непосредственно в буферы пользователя и многоступенчатое копирование будет исключено. Если приложением буферы заранее не подготовлены, информация заносится сервис-провайдером во внутренний буфер и система ждет запроса, чтобы перенести данные в буфер приложения. Исключением из этого правила является случай, когда приложение использует WSPSetSockOpt для установления нулевого размера входного буфера. В этом варианте данные принимаются лишь при наличии указателей на буфер приложения. При посылке информации клиенты для выдачи указателей на буфер данных используют WSPSend или WSPSendTo.

    В совмещенном режиме процедуры посылки и получения данных завершаются немедленно. Полученный по возврату нуль, указывает на то, что процедура завершилась успешно, то есть, сформирован соответствующий объект события или программа завершения установлена в очередь с помощью WPUQueueApc. Возврат флага SOCKET_ERROR, сопряженного с кодом ошибки WSA_IO_PENDING, указывает, что совмещенная операция успешно начата и позднее будет сообщено, когда выходной буфер будет свободен или входной буфер будет заполнен. Любые другие коды ошибок говорят о сбое.

    Совмещаться могут как операции ввода, так и вывода. Следует иметь в виду, что если было послано несколько блоков данных друг за другом, сообщения о завершении операций по их пересылке могут прийти в другом порядке. Тоже может произойти и при приеме данных.

    Сервис-­провайдеры имеют два способа сообщать о завершении совмещенных по времени операций: установка объекта события, заданного программой-клиентом, или запуск заданной клиентом программы завершения. В обоих случаях каждой из совмещенных операций ставится в соответствие структура WSAOVERLAPPED. Память для размещения этой структуры выделяется клиентом и используется им для указания того, какой из объектов следует установить при завершении процесса обмена. Структура WSAOVERLAPPED может использоваться сервис-провайдером для хранения дескриптора соответствующей совмещенной процедуры. Для извлечения нужной информации из указанной структуры можно воспользоваться оператором WSPGetOverlappedResult.

    Если параметр lpCompletionRoutine совмещенной операции не равен нулю, то сервис-провайдер может вызвать программу завершения, специфицированную клиентом. WinSock DLL предлагает асинхронную процедуру вызова (APC) программ завершения обмена.

    Для реализации той или иной функции в рамках сессии сервис-провайдер вызывает сначала процедуру WPUQueueApc. Сервис-­провайдер должен позаботиться о том, чтобы процесс, соответствующий выбранному контексту, был активным до вызова функции WPUQueueApc.

    WPUQueueApc берет в качестве входных параметров указатель на структуру WSATHREADID, указатель на процедуру APC и 32-разрядный контекстный код. Сервис-­провайдеры всегда получают указатель на соответствующую структуру WSATHREADID через параметр lpThreadId. Провайдер должен запомнить структуру WSATHREADID и выдать указатель на ее копию в качестве параметра оператора WPUQueueApc.

    Так как механизм APC работает только с одним 32-разрядным контекстным кодом, процедура APC не может быть сама программой завершения, заданной клиентом. Сервис­-провайдер должен выдать указатель на свою собственную процедуру APC, которая, используя контекстный код, обеспечивает доступ к необходимой информации для совмещенной операции обмена и вызывает заданную клиентом программу завершения.

    Сервис­-провайдер должен позволить клиентам WinSock 2 вызов процедур чтения или записи во время выполнения завершения обмена и гарантировать, что для данного сокета не будет допущено вложение операций завершения.

    Структура WSAOVERLAPPED создает коммуникационную среду между запуском совмещенной операции обмена и последующим ее завершением. Структура WSAOVERLAPPED совместима со структурой OVERLAPPED для Win32.

    Приоритетные данные могут доставляться пользователю независимо от обычной информации. Для коммуникационных протоколов, где предусмотрена пометка данных как срочные (например, TCP, позволяющий доставку срочной информации в общем потоке данных), система извлекает приоритетную информацию из общего потока и запоминает отдельно.

    Пользователь может определить, имеется ли приоритетная непрочитанная информация, используя процедуру WSPIoctl(SIOCATMARK). Для протоколов, где положение приоритетной информации в общем потоке имеет смысл, сервис-провайдер обеспечивает указатели, определяющие это положение. Для таких протоколов допускается обработка приоритетных данных в общем информационном потоке. Это достигается путем установки опции сокета SO_OOBINLINE (OOB OutOfBand). Для других протоколов, где блоки приоритетных данных независимы от общего информационного потока, попытка установить опцию SO_OOBINLINE вызовет ошибку. Приложение может использовать команду SIOCATMARK WSPIoctl для определения наличия непрочитанных блоков приоритетной информации.

    При запрещении SO_OOBINLINE ( disabled — значение по умолчанию):

  • сервис-провайдер WinSock сообщает клиенту о FD_OOB событиях, если клиент зарегистрирован для этого с помощью процедуры WSPAsyncSelect, точно так же FD_READ используется для сообщения о присутствии обычных данных. Таким образом, FD_OOB посылается, когда поступает приоритетная информация, а также когда данные прочитаны с использованием флага MSG_OOB, в условиях присутствия приоритетных данных, подлежащих чтению. Сообщение FD_READ для приоритетных данных не посылается;
  • сервис-провайдер WinSock возвращает соответствующий набор exceptfds при выполнении процедуры WSPSelect, если приоритетные данные присутствуют в очереди сокета;
  • клиент может вызвать WSPRecv с MSG_OOB для чтения блока приоритетных данных;
  • клиент может вызвать процедуру WSPRecv без MSG_OOB для чтения потока обычной информации. Блок приоритетных данных не может появиться в потоке обычных данных. Если приоритетные данные остаются после запроса WSPRecv, сервис-провайдер дает сообщение клиенту с помощью флага FD_OOB или через exceptfds при использовании запроса WSPSelect
  • для протоколов, где приоритетные данные находятся в потоке обычных данных, одного запроса WSPRecv недостаточно. Один вызов WSPRecv вернет обычные данные до маркера, и потребуется второй запрос WSPRecv для чтения данных после маркера.
  • Когда SO_OOBINLINE активировано ( enabled ):

  • сообщение FD_OOB не посылается для приоритетных данных, а процедуры WSPSelect и WSPAsyncSelect рассматривают эти данные как обычные, о типе информации можно судить по readfds сокета или по сообщению FD_READ, соответственно;
  • клиент не может осуществлять вызов WSPRecv с флагом MSG_OOB для чтения блока приоритетных данных — в противном случае будет получен код ошибки WSAEINVAL ;
  • клиент может вызвать WSPRecv без флага MSG_OOB. Любая приоритетная информация будет доставлена в потоке обычных данных. Приоритетные данные не будут никогда перемешаны с обычными данными, необходимо три запроса чтения для получения приоритетной информации. Первый запрос возвращает обычные данные, предшествующие приоритетным, второй — возвращает приоритетные данные, третий возвращает нормальные данные, следующие за приоритетными.
  • Программа WSPAsyncSelect прекрасно приспособлена для выявления приоритетных данных, когда SO_OOBINLINE находится в состоянии "выключено".

    Использование сокета несколькими процессами одновременно организовано следующим образом. Базовый процесс для получения специальной структуры WSAPROTOCOL_INFO вызывает WSPDuplicateSocket. Эта процедура для передачи структуры другому процессу применяет межпроцессный механизм коммуникаций (IPC). Последний процесс использует структуру WSAPROTOCOL_INFO при обращении к WSPSocket. Дескриптор сокета, полученный в результате этой операции, будет дополнительным дескриптором исходного сокета, который с этого момента может использоваться двумя процессами.

    Такой механизм разработан для того, чтобы удовлетворить как требованиям однопроцессной версии, так и многопроцессным вариантам Windows NT и XP. Следует иметь в виду, что совместное использование сокетов несколькими процессами возможно и без использования WSPDuplicateSocket, так как дескриптор сокета доступен для всех процессов.

    Когда формируется дескриптор нового сокета IFS-провайдер должен вызвать WPUModifyIFSHandle, а неIFS-провайдер должен вызвать WPUCreateSocketHandle (IFS Installable File System).

    Так как реально дублируются дескрипторы, а не сами сокеты, все параметры соединения оказываются абсолютно идентичными.

    Контроль состояния используемых совместно сокетов осуществляется посредством WSPAsyncSelect и WSPEventSelect. Вызов одного из указанных запросов с одним из дескрипторов сокета в качестве параметра аннулирует любую предшествующую регистрацию событий для указанного сокета, вне зависимости от того, какой из дескрипторов применен для новой регистрации. Таким образом, нельзя для процесса A иметь FD_READ -события, а для процесса B получать FD_WRITE -события.

    Процесс может вызвать WSPCloseSocket для дублирующего сокета и старый дескриптор будет ликвидирован, в то время как сам сокет, ему соответствующий, останется открытым вплоть до выполнения процедуры WSPCloseSocket.

    Два или более дескрипторов могут соответствовать одному и тому же сокету и использоваться независимо для операций ввода/вывода. Однако интерфейс WinSock не осуществляет какого-либо контроля за доступом, задача координации совместного использования сокета является объектом ответственности самих процессов.

    Для простоты в дальнейшем термин "многоточечный" будет означать широковещательный или мульти-кастинговый.

    В настоящее время многоточечные приложения (напр., IP-мультикастинг, STII, T.120, ATM UNI, и т.д.) значительно различаются по способу подключения узла к многоточечной сессии. Для декларации различных многоточечных атрибутов протокола в WinSock 2 используется структура WSAPROTOCOL_INFO. Просматривая эти атрибуты, программист может узнать, какие соглашения должны быть реализованы.

    В плоскости управления существует два типа различных сессий: rooted и nonrooted (корневые и некорневые). В случае корневого управления существует участник, называемый c_root, отличающийся от всех остальных членов многоточечной сессии, которые называются c_leaf (периферийные члены группы). Участник сессии c_root должен оставаться в списке участников на протяжении всей многоточечной сессии, так как без его участия сессия будет прервана. c_root обычно инициирует многоточечную сессию путем установления связей с участниками типа c_leaf. c_root может вводить членов в группу, но c_leaf может подключиться к c_root позднее.

    Для некорневой плоскости управления все участники многоточечной сессии являются периферийными узлами, и не существует какогото выделенного узла. Каждый c_leaf должен подключиться к многоточечной сессии, которая либо существует всегда (как в случае IP-мультикастинг адреса), либо создана за счет какого-то внешнего механизма. При другом подходе c_root по-прежнему существует, но принадлежит сети в целом (не является одним из участников сессии). Так как корневой узел существует, некорневая управляющая плоскость может рассматриваться как неявно корневая. Примерами такого рода неявно корневых схем являются система IP-мультикастинга — многоточечные блоки управления (Multipoint Control UnitMCU) в H.320-видеоконференциях и т.д.

    В плоскости данных также существует два стиля передачи информации: rooted и nonrooted (корневой и некорневой). В корневой плоскости данных имеется выделенный участник, называемый d_root. Обмен данными происходит исключительно между d_root и остальными участниками многоточечной сессии, которые называются d_leaf. Трафик может быть однонаправленным или двунаправленным. Данные, посланные d_root, будут доставлены всем d_leaf, в то время как данные, отправленные d_leafs, попадут только в d_root. В случае корневой плоскости данных не существует потока данных между периферийными узлами группы ( d_leaf ).

    В некорневой плоскости данных все участники эквивалентны и любая информация, посланная участником, будет доставлена всем членам группы (сессии). Аналогично, каждый узел d_leaf может получать данные ото всех остальных узлов группы, а также от любых узлов, не участвующих в данной сессии.

    В структуре WSAPROTOCOL_INFO имеется три поля атрибутов для выделения различных схем, используемых в плоскостях управления и данных.

  • XP1_SUPPORT_MULTIPOINT = 1 указывает на то, что этот протокол поддерживает многоточечные коммуникации, а последующие два поля имеют смысл.
  • XP1_MULTIPOINT_CONTROL_PLANE определяет, является ли плоскость управления корневой ( = 1 ) или не корневой ( = 0 ).
  • XP1_MULTIPOINT_DATA_PLANE указывает, является ли плоскость данных корневой ( = 1 ) или некорневой ( = 0 ).
  • В определенные моменты сокеты, включенные в многоточечную сессию, могут по своему поведению отличаться от сокетов типа точка­точка. Так, сокет вида d_leaf в корневой плоскости данных может только посылать информацию участнику d_root. Поэтому клиент должен иметь возможность заявить об этом на стадии формирования сокета. Делается это с помощью четырех многоточечных атрибутных флагов, которым присваивается определенное значение через параметр dwFlags в WSPSocket.

  • SA_FLAG_MULTIPOINT_C_ROOT служит для создания сокета, работающего как c_root. Это разрешено, если в WSAPROTOCOL_INFO указано, что существует контекст корневой плоскости управления.
  • WSA_FLAG_MULTIPOINT_C_LEAF предназначен для генерации сокета, работающего как c_leaf. Это возможно, если XP1_SUPPORT_MULTIPOINT присутствует в соответствующей записи WSAPROTOCOL_INFO.
  • WSA_FLAG_MULTIPOINT_D_ROOT предполагает формирование сокета, работающего как d_root. Это позволено, если в WSAPROTOCOL_INFO указано, что существует контекст корневой плоскости данных.
  • WSA_FLAG_MULTIPOINT_D_LEAF служит для создания сокета, работающего как d_leaf. Это допускается, если XP1_SUPPORT_MULTIPOINT присутствует в соответствующей записи WSAPROTOCOL_INFO.
  • Когда создается многоточечный сокет, один из двух флагов плоскости управления и один из двух флагов плоскости данных должны быть заданы в параметре dwFlags WSPSocket. Таким образом, при создании многоточечного сокета могут быть реализованы четыре возможности: c_root/d_root, c_root/d_leaf, c_leaf/d_root, или c_leaf/d_leaf.

    При использовании мультикастинга обычно необходимо специфицировать способ реализации сессии. Среди параметров сессии важную роль играет число вовлеченных сетевых сегментов. Для регулирования этого числа используется команда SIO_MULTICAST_SCOPE WSPIoctl. Нулевое значение числа сетевых сегментов означает, что мультикастинг-­пакеты не покинут пределы ЭВМ и могут попадать только во внутренние сокеты. Значение единица (число по умолчанию) указывает, что мультикастинг­-пакеты не могут выйти за пределы маршрутизатора локальной сети. Большие величины позволят распространение сообщений, проходящих соответствующее число маршрутизаторов. Этот код идентичен параметру времени жизни (TTL) в IP-мультикастинге.

    Многоточечный сокет часто характеризуется его ролью в плоскости данных и управления. Следует иметь в виду, что один и тот же сокет может иметь совершенно разную роль в разных плоскостях.

    В схемах для корневой плоскости периферийные узлы добавляются в многоточечную группу одним из двух способов. В первом методе корень использует WSPJoinLeaf для инициализации соединения с периферийным узлом и приглашает его быть участником сессии. Со стороны периферийного узла приложение должно создать c_leaf -сокет и использовать WSPListen для установки его в режим ожидания (listen). Периферийный узел получит сообщение FD_ACCEPT, которое означает приглашение присоединиться к многоточечной сессии, и может объявить о своем желании подключиться путем вызова WSPAccept. Корневое приложения получит сообщение FD_CONNECT, когда операция подключения к группе завершена.

    Во втором методе роли меняются. Корневой клиент создает сокет c_root и переходит в режим ожидания ( listen ). Периферийный узел, желающий присоединиться к сессии, создает сокет c_leaf и запускает WSPJoinLeaf, чтобы инициализировать подключение и доступ. Корневой клиент получает FD_ACCEPT, когда приходит запрос доступа, и принимает периферийный узел в группу посредством запроса WSPAccept. Периферийный узел получает FD_CONNECT, когда он принят в группу. Для случая IP-мультикастинга это эквивалентно опции сокета IP_ADD_MEMBERSHIP. Читателей, знакомых с использованием несвязного UDP-протокола в IP-мультикастинге, может смутить присутствующая здесь семантика, ориентированная на соединение. В частности, указание на то, что используется запрос WSPJoinLeaf для сокета UDP и ожидание сообщения FD_CONNECT могут вызвать недоразумение. Существуют уже, однако, прецеденты использования такой семантики для протоколов, не ориентированных на соединение. Разрешено и иногда полезно, например, использовать процедуру WSPConnect для UDP-сокетов. Общим результатом применения семантики, ориентированной на соединение для несвязных сокетов, является ограничение на то, как эти сокеты могут использоваться. UDP-сокет, реализуемый в WSPJoinLeaf, будет иметь определенные ограничения, а ожидание сообщения FD_CONNECT (которое в этом случае указывает на посылку соответствующего IGMP-сообщения) является одним из таких ограничений. Существует три случая, когда клиент может использовать WSPJoinLeaf.

  • При работе в качестве многоточечного сервера (root) приглашает новый периферийный узел принять участие в сессии.
  • При работе в качестве периферийного узла выполняется запрос подключения к корневой многоточечной сессии.
  • При работе в качестве периферийного узла запрашивается подключение к некорневой многоточечной сессии (например, при IP-мультикастинге).
  • Как было упомянуто ранее, WSPConnect, за исключением того, что он возвращает дескриптор сокета (как и WSPAccept ), и имеет дополнительный параметр dwFlags. Параметр dwFlags показывает, будет ли сокет работать только в режиме чтения, только в режиме записи или в обоих режимах. Только многоточечный сокет может использоваться в качестве входного параметра s этой процедуры. Если многоточечный сокет находится в неблокирующем состоянии, полученный дескриптор сокета нельзя будет использовать до тех пор, пока не будет получено сообщение FD_CONNECT. Корневое приложение в многоточечной сессии может вызвать WSPJoinLeaf один или более раз, чтобы добавить новые узлы к текущей многоточечной сессии.

    Параметры дескриптора сокета, присланного WSPJoinLeaf, варьируются в зависимости от того, является ли дескриптор c_root или c_leaf. Для c_root сокета параметр name означает имя определенного периферийного узла, который должен быть добавлен, а возвращенный дескриптор сокета является c_leaf и соответствует вновь добавленному периферийному узлу. Некоторые многоточечные приложения могут позволять этому сокету "постороннее" общение с корневым сервером.

    При вызове WSPJoinLeaf для сокета c_leaf, параметр name содержит адрес корневого приложения (для схемы корневого управления) или существующей многоточечной сессии (некорневая схема управления), и возвращенный дескриптор сокета является тождественным по отношению ко входному дескриптору сокета. В корневой схеме управления корневой клиент должен поставить свой c_root сокет в режим ожидания с помощью WSPListen. При запросе периферийного узла о присоединении к группе присылается стандартное сообщение FD_ACCEPT. Корневой клиент использует для подключения нового периферийного узла обычную процедуру WSPAccept. Запрос WSPAccept присылает дескриптор сокета c_leaf, точно так же, как и WSPJoinLeaf.

    Многоточечный корневой клиент является ответственным за прерывание сессии. Такое приложение может применить WSPShutdown или WSPClosesocket для сокета c_root, чтобы прислать всем членам группы сокетов c_leaf сообщение FD_CLOSE.

    Рассмотрим семантическое отличие многоточечных и обычных сокетов. В плоскости управления имеется существенное семантическое отличие между сокетами c_root и обычными сокетами точка­-точка.

  • Сокет c_root может использоваться в процедуре WSPJoinLeaf для подключения новых периферийных узлов.
  • Постановка сокета c_root в режим ожидания (путем вызова WSPListen ) не препятствует тому, чтобы сокет c_root применялся оператором WSPJoinLeaf для добавления в список участников нового периферийного узла или для посылки или получения информации.
  • Закрытие сокета c_root вызовет отправку всем сопряженным сокетам c_leaf сообщения FD_CLOSE.
  • Не существует какого-либо семантического различия между сокетом c_leaf и традиционным сокетом в плоскости управления, за исключением того, что сокет c_leaf может использоваться процедурой WSPJoinLeaf, а использование сокета c_leaf в WSPListen указывает на то, что должны восприниматься только запросы многоточечного соединения.

    В плоскости данных семантическое отличие между сокетами d_root и традиционными сокетами заключается в следующем:

  • данные, посланные на сокет d_root, будут доставлены всем членам группы узлов, участвующих в многоточечном обмене;
  • данные, полученные сокетом d_root, могут поступить от любого участника многоточечного обмена.
  • Сокет d_leaf в корневой плоскости данных не имеет каких-­либо семантических отличий от традиционных сокетов, однако в некорневой плоскости данных информация, посланная на сокет d_lea f, поступит ко всем периферийным узлам группы. Данные могут передаваться любым участником многоточечной сессии. Информация о том, находится ли сокет d_leaf в корневой или некорневой плоскости данных, хранится в структуре сокета WSAPROTOCOL_INFO.

    Следует иметь в виду, что перед написанием программы, использующей технологию сокетов, нужно сначала познакомиться с описанием библиотеки, которую вы собираетесь использовать.

    Страницы:

    Какие бы замечательные идеи в области телекоммуникаций, распределенных баз знаний или поисковых систем вам не пришли в голову, реализовать их на практике можно, лишь написав соответствующую программу. Основные операционные среды (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-адрес;

    удаленный IP-адрес;

    номер локального порта;

    номер удаленного порта.

    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),

    где accept и переходит в режим ожидания запросов. Программа извлекает первый элемент очереди, создает новый сокет со свойствами, идентичными s, и при успешном выполнении возвращает дескриптор нового сокета. При возникновении ошибки возвращается код INVALID_SOCKET. По окончании обработки запроса сервер вновь вызывает accept, который возвращает ему дескриптор сокета очередного запроса, если таковой имеется. Если очередь пуста, accept блокирует программу до получения связи. Существуют серверы с параллельной и последовательной обработкой запросов. Параллельный обработчик запросов не ждет завершения обработки предшествующего запроса и вызывает оператор accept немедленно. В системе Unix используются обычно параллельные обработчики запросов.

    Возможная схема использования сокетов в случае работы N клиентов с одним WEB-сервером показана на рис. 4.1. Клиент 1 сформировал два соединения с сервером.

    (рис 4.1) Формирование сокетов при подключении к одному WEBсерверу

    Схема взаимодействия различных операторов winsock в рамках идеологии клиент/сервер для случая процедур, ориентированных на соединение, показана на рисунке 4.2. Горизонтальными стрелками обозначены направления посылки сетевых сообщений.

    Следует иметь в виду, что программе клиента (выделена рамкой) в этом режиме не нужно знать номер порта, поэтому она не обращается к процедуре bind, а для установления связи сразу вызывает оператор connect. Современные распределенные информационные системы, WWW-серверы, поисковые системы и т.д. эффективно используют механизмы формирования сокетов и многие процедуры, описанные в данном разделе. Из литературы [2.15, 2.24] известно, что для многих видов услуг в Интернет выделены строго определенные номера портов. Доступ же к этим услугам должен быть обеспечен достаточно большому числу пользователей. С клиентской стороны при этом используются номера портов со значениями из диапазона 1024-5000. Для каждого нового клиентского запроса в ЭВМ-сервере, как правило, формируется новый процесс.

    (рис 4.2) Схема взаимодействия операторов winsock для процедур, ориентированных на соединение

    Лишь при успешной реализации всех перечисленных операций может начаться обмен данными. Для пересылки информации могут использоваться команды write, read, send, recv. Команды write и read имеют форму вызова:

    R=write(s, buf, len) или R=read(s, buf, len),

    где s — дескриптор сокета, buf — имя массива, подлежащего пересылке (или предназначенного для приема), len — длина этого массива. Оператор writev отличается от write тем, что данные могут не лежать в виде непрерывного массива:

    R=writev(s, io_vect, vectlen) или R=readv(s, io_vect, vectlen),

    где s — дескриптор сокета, io_vect — вектор указатель на список указателей, vectlen — длина списка указателей. Команда выполняется медленнее, чем write или read. Список указателей имеет формат (рис. 4.3):

    (рис 4.3) Формат списка указателей для функций readv и writev

    Команды send(s, msg_buf, buflen, flags) и recv имеют аналогичный формат, но среди параметров обращения содержат переменную flags, которая служит для целей диагностики и управления передачей данных (например, пересылка информации с высоким приоритетом ( MSG_OOB — Message Out Of Band ), что используется, в частности, при передаче звуковых сообщений). При работе с операторами send или recv надо быть уверенным, что принимающая сторона знает, что ей следует делать с этими приоритетными сообщениями. Другой возможный флаг, определяемый константой MSG_PEEK, позволяет анализировать запросы из входной очереди транспортного уровня. Обычно после считывания данных из входной очереди они уничтожаются. Когда MSG_PEEK=1, данные из входной очереди не стираются. Этот флаг используется, например, программой FTP. При успешном выполнении команды будет возвращено число переданных байтов, в противном случае —1.

    Все перечисленные выше операторы рассчитаны на применение в рамках протоколов, ориентированных на установление соединения (TCP), где не требуется указание адреса места назначения. В протоколах типа UDP (не ориентированных на соединение) для передачи информации используются операторы sendto, recvfrom или sendmsg:

    R=sendto(s, msg_buf, buflen, flags, adr_struc, adr_struc_len)

    или recvfrom(s, msg_buf, buflen, flags, adr_struc, adr_struc_len),

    где s — дескриптор сокета, msg_buf — указатель на буфер, где лежит сообщение, buflen — длина этого буфера (длина сообщения), adr_struc — адресная структура, содержащая исчерпывающую информацию об адресате, adr_struc_len — длина этой структуры. Оператор recvfrom принимает все данные, приходящие на его порт. Приняв дейтограмму, recvfrom записывает также адрес, откуда эта дейтограмма получена. Сервер может посылать по этому адресу дейтограмму­-отклик. Вызов оператора sendmsg имеет форму:

    R=sendmsg(s, msg_struc, flags) [или recvmsg(s, msg_struc, flags)],

    где s — дескриптор сокета, msg_struc — информационная структура, формат которой показан ниже на рисунке 4.4. Применение структур делает программирование пересылки сообщений более гибким. Следует учитывать, что для обменов, не ориентированных на соединение, сокет как бы состоит лишь из одной половины (IP-адрес и номер порта). Сокеты, созданные однажды для обмена (UDP), далее могут жить своей жизнью. Они могут принимать пакеты от других аналогичных "сокетов" и сами посылать им дейтограммы (кавычки здесь связаны с тем, что это не реальный сокет и никакого соединения здесь не осуществляется).

    (рис 4.4) Формат информационной структуры msg_struc

    Взаимодействие операторов winsock для систем, не ориентированных на соединение, показано на рисунке 4.5. Здесь так же, как и в случае, ориентированном на соединение, сервер вызывает socket и bind, после чего обращается к процедуре recvfrom (вместо read или recv ). Программа-клиент в данной схеме обращается к оператору bind и совсем не использует оператор connect (ведь предварительного соединения не нужно). Для передачи запросов и приема откликов здесь служат операторы sendto и recvfrom, соответственно.

    Помимо уже описанных операторов для работы с сокетами имеется еще один — select позволяет процессу отслеживать состояние одного или нескольких сокетов. Для каждого сокета вызывающая программа может запросить информацию о статусе read, write или error. Форма обращения имеет вид:

    (рис 4.5) Схема взаимодействия операторов winsock для процедур, не ориентированных на соединение
    R=select(num_of_socks, read_socks, write_socks, error_socks, max_time),

    где num_of_socks — число контролируемых сокетов (в некоторых реализациях не используется и является необязательным; по умолчанию это число не должно превышать 64).

    В версии Беркли read_socks, write_socks и error_socks представляют собой побитовые маски, определяющие тип сокета. Параметр read_socks — это указатель на структуру, описывающую набор сокетов, состояние которых проверяется на возможность чтения (версия winsock ). Если сокет находится в состоянии listen, он будет помечен как "готов для чтения", при условии, что запрос на соединение уже получен. Это предполагает выполнение оператора accept без блокировки. Для других сокетов "готовность к чтению" подразумевает наличие в очереди запросов чтения. Для сокетов типа SOCK_STREAM это означает, что виртуальный сокет, соответствующий данному сокету, закрылся и операторы recv или recvfrom будут выполнены без блокировки. Если виртуальное соединение закрыто корректно, оператор recv вернет код 0, в противном случае (например, принудительное закрытие) будет возвращен код WSAECONNRESET. Параметр write_socks — указатель на набор сокетов, состояние которых проверяется на возможность записи. Если сокет находится в процессе выполнения процедуры connect, "способность к записи" означает, что установление связи завершено. Для других сокетов это значит, что операции send или sendto будут выполнены без блокировки.

    Параметр error_socks — это указатель на набор сокетов, проверяемых на ошибки. В некоторых реализациях этот аргумент идентифицирует список сокетов, помеченных как приоритетные. Сокет помечается как приоритетный, если опция SO_OOBINLINE=FALSE. В случае ошибки оператор select отмечает сокет, где это произошло. Select работает лишь с теми сокетами, которые были выделены с помощью масок. При успешном выполнении оператор возвращает число сокетов, готовых к операциям ввода/вывода, и модифицирует коды масок в соответствии с состоянием сокетов. Прикладная программа может использовать результаты вызова оператора select, анализируя полученные коды масок. Аргумент max_time определяет максимальное время, выделенное select для завершения своей работы. Для уточнения типа ошибки, возникшей при исполнении операции select, можно воспользоваться процедурой WSAGetLastError.

    Другим важным оператором является closesocket(s), который закрывает канал сокета с одной из сторон. Все описанные выше операторы (кроме socket, bind и listen ) блокируют работу программы до своего завершения. Практически любая операция, непосредственно связанная с выполнением процедур ввода/вывода, может блокировать выполнение других прикладных функций winsock.

    Для обслуживания прикладных процессов (например, WWW-сервера, работа с распределенными базами данных и пр.) разработано много других сервисных программ (WINSOCK.DLL), перечень которых представлен в таблице 4.1.

    Большинство перечисленных команд имеют развитую систему диагностики, кроме того, во многих реализациях Unix существует много других полезных команд, описание которых вы можете найти в инструкциях по использованию системы Unix. Рассмотрим некоторые из них.

    Программа ioctlsocket(s, long cmd, u_long FAR*argp) служит для получения параметров сокета (выполнение не зависит от типа протокола и коммуникационной субсистемы). Аргумент cmd представляет собой код команды, которая будет выполнена для сокета s, argp — указатель на параметр команды. Возможно применение команд: FIONBIO — разрешает/запрещает режим блокировки сокета s (команда WSAAsyncSelect ставит сокет в режим запрета блокировок автоматически); FIONREAD — определяет объем данных, которые могут быть автоматически считаны через сокет s ; SIOCATMARK — задает режим чтения приоритетной информации (для сокетов типа SOCK_STREAM ).

    Перечень служебных операторов для работы с сокетами (Беркли)
    Имя команды назначение
    getdomainname Возвращает имя домена
    gethostbyname Возвращает IP-адрес для заданного сетевого имени
    gethostname Возвращает имя ЭВМ (обычно имя ее домена)
    gethostadr Возвращает IP-адрес ЭВМ
    getnetaddr Возвращает адрес сети
    getnetname Возвращает имя сети
    getpeername Возвращает имя партнера, подключенного к сокету
    getportbyname Возвращает имя и код протокола для указанного имени (например, ICMP, UDP или TCP)
    getportbynumber Возвращает имя протокола для указанного его кода
    getservbyname Извлекает из базы данных название протокола и номер порта для указанного имени сетевой услуги
    getservbyport Возвращает имя сетевой услуги для заданного номера порта
    getsockname Возвращает местный адрес (имя) сокета
    getsockopt Запрашивает информацию о сокете
    htonl Преобразует порядок байтов 32-разрядного кода из машинного в сетевой
    htons Преобразует порядок байтов 16-разрядного кода из машинного в сетевой
    inetaddr Преобразует символьную строку IP-адреса из десятично-точечного формата в 32-разрядный код с сетевым порядком байтов
    inet ntoa Преобразует IP-адрес в десятично-точечный формат
    ioctlsocket Управляет параметрами сокета, связанными с обработкой операций ввода/вывода
    ntohl Преобразует порядок байтов 32-разрядного кода из сетевого в машинный
    ntohs Преобразует порядок байтов 16-разрядных кодов из сетевого в машинный
    ethostname Устанавливает имя ЭВМ
    setsockopt Устанавливает опции сокета
    shutdown Закрывает один из концов дуплексного канала для местной ЭВМ
    socketpair Генерирует пару сокетов

    Программа setsockopt(s, int level, int optname, const char far*optval, int optlen) устанавливает текущие значения опций для сокета s. Аргумент level описывает уровень, на котором определена данная опция (например, SOL_SOCKET или IPPROTO_TCP ); optnameимя опции, значение которой устанавливается, optval — указатель на буфер, где лежит значение опции, optlen — размер этого буфера. Для опции SO_LINGER — это размер структуры, для остальных — длина целого. При корректном исполнении setsockopt возвращает нуль, в противном случае — SOCKET_ERROR. Программа setsockopt поддерживает следующие опции ( BSD поддерживает и некоторые другие опции; колонка тип соответствует значению optval, таблица 4.2):

    Опции сокетов для оператора setsockopt
    опция Тип назначение
    SOBROADCAST булев Позволяет передачу широковещательных сообщений
    SODEBUG булев Осуществляет запись отладочных данных
    SO DONTLINGER булев Разрешает закрытие без ожидания при наличии не отосланной информации. Эта опция эквивалентна SO_LINGER с l_onoff=0
    SODONTROUTE булев Запрет маршрутизации — отправка непосредственно интерфейсу
    SOKEEPALIVE булев Посылка сообщения keepalive ("еще жив")
    SO LINGER структура Задержка закрытия в случае наличия не отосланной информации
    SOOOBINLINE булев Принимает информацию, приходящую по независимым каналам, в общем потоке данных
    SO RCVBUF целый Определяет размер входного буфера
    SO REUSEADDR булев Позволяет сокету использовать адрес, который уже задействован
    SO SNDBUF целый Определяет размер выходного буфера
    TCP NODELAY булев Запрещает использование алгоритма Нагля (TCP).

    Программа getsockopt(s, int level, int optname, char far*optval, int FAR* optlen) позволяет получить значение опции для любого типа сокетов. Значения параметров обращения аналогичны setsockopt. Ниже представлена таблица 4.3 поддерживаемых опций.

    В среде Windows существуют аналоги (асинхронные) многих из приведенных выше операторов. Имена этих операторов имеют префикс WSA (Windows Socket Asynchronous). Асинхронными они названы по той причине, что их выполнение сопряжено с определенным диалогом и ни начало, ни завершение не ограничено какими-либо временными рамками. Список таких операторов представлен в таблицах 4.4 и 4.5 (версия windows socket 2.2).

    Опции сокетов для оператора getsockopt
    опция Тип назначение
    SO ACCEPTCONN булев Сокет в режиме listen
    SOBROADCAST булев Разрешена передача широковещательных сообщений
    SO DEBUG булев Отладочный режим разрешен
    SO DONTLINGER булев Если равен TRUE, SOLINGER-опция запрещена
    SO DONTROUTE булев Запрет маршрутизации
    SO ERROR целое Выдает статус ошибок
    SOKEEPALIVE булев Сообщение keepalive ("еще жив") послано
    SO LINGER структура Возвращает текущие значения опции SO_LINGER
    SOOOBINLINE булев Принимает информацию, приходящую по независимым каналам, в общем потоке данных
    SO RCVBUF целый Сообщает размер входного буфера
    SO REUSEADDR булев Сокету разрешено использовать адрес, который был задействован
    SO SNDBUF целый Сообщает размер выходного буфера
    SOTYPE целый Тип сокета (например, SOCK_ STREAM )
    TCP NODELAY булев Использование алгоритма Нагля запрещено (tcp)

    Из этого списка можно выделить две программы WSAStartup и WSACleanup, первая вызывается в начале любой процедуры, вторая — ее завершает. WSAStartup может вызываться за время сессии несколько раз, она позволяет указать версию winsock или получить информацию о ее конкретной реализации. При вызове WSAStartup осуществляется диалог с динамической библиотекой WINSOCK.DLL и настройка параметров системы. При аварийном завершении программы нужно корректно окончить работу с WINSOCK.DLL. Следует при этом помнить, что WSACleanup воздействует на все потоки завершаемого процесса (например, в случае Windows). Определенные проблемы может вызвать перенос программ из Unix в Windows, так как там вместо read и write используются операторы recv и send, вместо ioctlioctlsocket, а вместо closeclosesocket. Некоторые операторы вообще непереносимы: readv, writv, recvmsg и sendmsg и части программы, где они содержатся, необходимо переписать. При обнаружении ошибки Unix присваивает переменной errno соответствующее значение. В winsock для этой цели используется символьная константа SOCKET_ERROR (равная 1), а для уточнения типа ошибки следует вызвать WSAGetLastError. В системе Windows этот оператор обращается к программе Win32 GetLastError, которая возвращает значение ошибки для сессии, вызвавшей эту ошибку.

    Основные операторы winsock
    Имя оператора назначение
    WSAAsyncGetHostByAddr Аналог оператора gethostbyaddr
    WSAAsyncGetHostByName Аналог оператора gethostbyname
    WSAAsyncGetProtoByName Аналог оператора getprotobyname
    WSAAsyncGetProtoByNumber Аналог оператора getprotobynumber
    WSAAsyncGetServByName Аналог оператора getservbyname
    WSAAsyncGetServByPort Аналог оператора getservbyport
    WSAAsyncSelect Функциональный аналог оператора select
    WSACancelAsyncRequest Прерывает выполнение операторов типа WSAAsyncgetХbyY
    WSACancelBlockingCall Прерывает выполнение блокирующего оператора приложения (API)
    WSACleanup Сообщает Windows sockets о завершении работы программы с DLL
    WSAGetLastError Выдает сообщение о последней ошибке
    WSAIsBlocking Определяет, является ли библиотека Winsock DLL блокирующей
    WSASetBlockingHook Устанавливает перехват блокирующего вызова
    WSASet LastError Фиксирует тип ошибки для последующего вызова WSALastError
    WSAStartup Инициализирует следующий уровень Winsock
    WSAUNhookBlockingHook Восстанавливает прежнюю блокировку.
    Асинхронные операторы
    WSAAccept* Расширенная версия accept, которая разрешает условное подключение и формирование групп сокетов
    WSACloseEvent Уничтожает объект события
    WSAConnect* Расширенная версия connect, которая позволяет обмениваться данными о соединении и QoS-информацией.
    WSACreateEvent Создает объект события
    WSADuplicateSocket Создает новый дескриптор сокета для случая использования его несколькими процессами
    WSAEnumNetworkEvents Выявляет сетевые события
    WSAEnumProtocols Выдает информацию о каждом доступном протоколе
    WSAEventSelect Связывает сетевое событие с объектом события
    WSAGetOverlappedResult Сообщает состояние выполнения совместной операции обмена
    WSAGetQOSByName Выдает параметры QOS для заданного имени сетевой услуги
    WSAHtonl Расширенная версия htonl
    WSAHtons Расширенная версия htons
    WSAIoctl* Версия ioctlsocket, пригодная для совмещения процедур ввода/вывода
    WSAJoinLeaf* Подключает периферийный узел к многоточечной сессии
    WSANtohl Расширенная версия ntohl
    WSANtohs Расширенная версия ntohs
    WSARecv* Расширенная версия recv, которая позволяет совмещать во времени операции ввода/вывода, использовать многобуферную схему при работе с векторами указателей и получать флаги в качестве входных и выходных параметров
    WSARecvDisconnect Завершает работу сокета и выдает информацию о завершении, если сокет был ориентирован на работу в связанном состоянии
    WSARecvErom* Расширенная версия recvfrom, которая позволяет совмещать по времени операции ввода/ вывода, использовать многобуферную схему при работе с векторами указателей и получать флаги в качестве входных и выходных параметров
    WSAresetevent Обнуляет объект события
    WSASend* Расширенная версия send, которая позволяет совмещать по времени операции ввода/вывода, использовать многобуферную схему при работе с векторами указателей
    WSASendDisconnect Инициализирует процедуру закрытия соединения и опционно посылает сообщение disconnect
    WSASendTo* Расширенная версия sendto, которая позволяет совмещать по времени операции ввода/вывода, использовать многобуферную схему при работе с векторами указателей
    WSASetevent Устанавливает объект события
    WSASocket Расширенная версия socket, которая использует структуру WSAPROTOCOL_INFO в качестве входной информации и позволяет создать сокеты, работающие одновременно. Позволяет также формировать группы сокетов
    WSAWaitForMultiple Events Присылается, если любой или все специфицированные объекты находятся в сигнальном состоянии или когда истекает время тайм-аута
    * Программа может вызвать блокировку при работе с блокирующим соке-том.

    При написании программ на персональной ЭВМ, например, на С++ или ассемблере, существует возможность выбора модели памяти: small, medium, compact, large или huge. Эти модели отличаются друг от друга числом сегментов (по 64 Кбайт) памяти, выделяемой для программы и данных. Модель small предполагает, что все указатели имеют тип near, если явно не указано обратное (не записан модификатор FAR). Модель large считает по умолчанию все указатели дальними, если явно не присутствует модификатор near. В модели huge длина блока данных может превышать размер одного сегмента. С особенностями этих моделей рекомендуется ознакомиться по описаниям конкретных трансляторов. Используя те или иные модели при создании сетевых программ, нужно учитывать проблемы, которые могут возникнуть при переносе вашей программы из одной операционной среды в другую. ОС Unix и Windows не поддерживают каких­-либо моделей памяти, и их система адресации скорее совместима с моделью large. При переносе программы из одной среды в другую об этой проблеме лучше не забывать, и перед началом отладки полезно изучить особенности адресации.

    Различие операционных сред особенно заметно при выполнении блокирующих процедур, рассмотренных выше, например, операции сетевого ввода/вывода. Блокирующие процедуры являются потенциальным источником "повисания" программ — скажем, оператор recv может вечно ждать отклика, в то время как удаленный сервер будет ждать сообщения от программы, выполняющей recv. По этой причине создание не блокирующих сокетов представляется привлекательным. Такие сокеты формируются путем вызова стандартного оператора socket с последующим обращением к процедуре, изменяющей режим работы сокета (по умолчанию создается блокирующий сокет). После этого при вызове блокирующего оператора, например, recv, при условии, что сокет не имеет запрашиваемых данных, система возвратит флаг ошибки. Таким образом, при работе с неблокирующим сокетом операционная система проверяет возможность немедленного выполнения процедуры и возвращает сигнал ошибки, если это невозможно. Все операторы winsock являются асинхронными и неблокирующими. Асинхронные операции при невозможности выполнить требуемую операцию не выдают сообщения об ошибке. За тем, чтобы процедура была выполнена так, как нужно, в этом случае следит операционная система. По завершении асинхронной операции ОС Windows посылает сообщение тому окну, из которого эта операция была вызвана. Но и операторы, работающие с неблокирующими сокетами Беркли, также являются неблокирующими. В то же время все операции ввода и вывода в Unix являются синхронными.

    Хотя оператор WSAAsyncSelect считается аналогом select, между ними имеется существенное отличие. WSAAsyncSelect — единственный оператор, использующий дескриптор сокета в качестве параметра. Если select контролирует состояние нескольких сокетов, то для того, чтобы достичь того же результата с помощью wsaasyncselect, надо реализовать столько вызовов, сколько сокетов мы хотим мониторить. Форма обращения к WSAAsyncSelect имеет вид:

    WSAAsyncSelect (SOCKETs, HWND hWnd, unsigned int wMsg, long lEvent),

    где s — дескриптор сокета, состояние которого мы хотим контролировать, аргумент, hWnd — дескриптор окна получателя сообщения; wMsg определяет тип посылаемого сообщения (эти два параметра являются стандартными для всех функций Windows); lEvent — битовая маска, определяющая тип событий, которые нас интересуют. Возможные значения параметра lEvent приведены в таблице 4.6.

    Возможные значения параметра lEvent оператора WSAAsyncSelect
    значение назначение
    FD READ Готовность к чтению
    FD WRITE Готовность к записи
    FD OOB Поступление Out_Of_Band данных
    SD ACCEPT Контроль установки входного соединения
    FD CONNECT Контроль реализованных соединений
    FD CLOSE Контроль закрытия соединения

    При необходимости контроля комбинации вышеперечисленных состояний эти маски могут объединяться по ИЛИ. Как только состояние сокета станет соответствовать выбранной маске, Windows пошлет прикладной программе соответствующее сообщение. Это сообщение содержит дескриптор окна, откуда осуществлен вызов процедуры WSAAsyncSelect, идентификатор сообщения, а также 16-битовый и 32-битовый параметры этого сообщения. Первый из них представляет собой дескриптор сокета, где это событие произошло. Младшие 16 бит второго параметра являются кодом события, а старшие предназначены для записи кода ошибок, если они произошли.

    Как уже было отмечено, обращение к WSAAsyncSelect переводит сокет в неблокирующее состояние. При необходимости реализовать, например, процедуру recv, следует обратиться сначала к WSAAsyncSelect, запросив Windows информировать вас о готовности чтения ( lEvent=FD_READ ). После этого обработчик сообщений windows при получении соответствующего сигнала предоставит возможность прикладной программе перейти к выполнению операции recv. Так как сокет уже получил данные, блокировки не произойдет и программа их немедленно получит.

    Особое внимание блокирующим операциям должно быть уделено в старых версиях Windows, поскольку блокировка остановит не только выполнение задачи, вызвавшей эту операцию, но и все другие приложения (ЭВМ станет неуправляемой на время блокировки). Здесь блокирующие процедуры должны быть запрещены, вместо этого при необходимости вызова такой процедуры windows реализует цикл проверки состояния сокета (очереди сообщений). Остальные приложения могут при этом продолжить свою работу, а при получении благоприятного сообщения Windows разрешает выполнение блокирующей процедуры, так как она уже не может вызвать блокировки. Осложнения могут возникнуть, если обработчик сообщений Windows получит сигнал, который приведет к вызову другой блокирующей процедуры. Сегодня в Windows действует правило, запрещающее такие вызовы из прикладных программ, как блокирующие, так и неблокирующие. То есть, на время выполнения блокирующей процедуры все обращения к сетевому интерфейсу запрещены.

    Имеется несколько операторов Winsock, предназначенных для работы с блокирующими процедурами (смотри таблицу 4.7).

    WSACancelBlockingCall прерывание блокирующей процедуры (без аргументов)
    WSAIsBlocking определение блокирующей операции (без аргументов)
    WSASetBlockingHook перехват блокирующего вызова, организация цикла ожидания
    WSAUNhookBlockingHook восстановление прежней блокировки

    При необходимости прервать блокирующую операцию можно вызвать процедуру WSACancelBlockingCall, прикладная программа получит при этом сообщение об ошибке ( WSAEINTR ). Оператор WSAIsBlocking возвращает значение TRUE, если в данный момент реализуется блокирующая операция. Последние два оператора из четырех названных служат для построения пользовательских обработчиков сообщений.

    Аппарат сокетов предполагает возникновение и исчезновение вычислительных и управляющих процессов. Новый процесс может наследовать "старые" сокеты. В этом случае может возникнуть необходимость выяснить адрес партнера, с которым взаимодействует данный сокет. Эту задачу можно решить с помощью команды getpeername(s, destaddr, addrlen), где destaddr — указатель на структуру типа (рис. 4.6):

    (рис 4.6) Указатель на структуру типа для команды getpeername

    AF идентифицирует семейство протоколов (для TCP/IP=2 ), для которого порожден данный сокет, вся структура занимает 16 октетов. addrlen — указатель на переменную, куда будет записана длина адреса. Сокет может быть выключен командой close(s), где s — идентификатор сокета, который надлежит закрыть. Если пользователь не хочет более посылать или получать данные, он может выдать команду shutdown(s, how), где параметр how может принимать значения: 0 — блокируется прием данных; 1 — блокируется передача данных; 3 — блокируются любые обмены.

    Каждое соединение должно иметь свой неповторимый код ISN (Initial Sequence Number). Этот код посылается клиентом серверу с помощью сегмента SYN. Для реализации режима соединения прикладная программа на одном конце канала устанавливается в режим пассивного доступа (passive open), а операционная система на другом конце ставится в режим активного доступа (active open). Протокол TCP предполагает реализацию 11 состояний ( ESTABLISHED, CLOSED, LISTEN, SYN_SENT, SYN_RCVD и т.д.), переход между которыми строго регламентирован (см. описание протокола ТСР выше).

    При написании диагностических и управляющих программ в рамках ОС Windows можно использовать простые сокеты (Sock_Raw) или библиотеку ICMP.DLL (эта динамическая библиотека не является частью Win32 API). Библиотека ICMP.DLL содержит, в частности, процедуру ICMPSendEcho, которая посылает запросы эхо по указанному адресу и возвращает отклик в пределах указанного временного интервала. В качестве аргументов запрос ICMPSendEcho использует ICMP-дескриптор, который получается в результате запроса IcmpCreateFile.

    Приложение может использовать WSAEnumProtocols для определения того, какой транспортный протокол (стек протоколов) поддерживается, и попутно можно получить дополнительную информацию, которая содержится в структуре WSAPROTOCOL_INFO.

    В то время как в WinSock 1.1 имеется только одно семейство адресов AF_INET, включающее в себя ограниченное число известных типов сокетов и идентификаторов протоколов, в WinSock 2 это ограничение снято. Информация по WinSock 2 доступна по адресу:

    http://www.ictp.trieste.it/~radionet/nuc1996/ref/winsock/wsspi22.htm

    В настоящее время WinSock допускает совмещение по времени нескольких операций ввода/вывода. Такого рода операции возможны только для сокетов, созданных оператором WSASocket с флагом WSA_FLAG_OVERLAPPED=1 (Win32).

    Запросы получения и отправки информации возвращают отклик немедленно. Если получен нуль, это означает, что операция ввода/вывода завершилась успешно; если же получен флаг SOCKET_ERROR с кодом ошибки WSA_IO_PENDING — операция начата успешно, последующие сообщения позволят судить о дальнейшем выполнении операции.

    Допуская возможность нескольких операций ввода/вывода одновременно, нужно обеспечить соответствие между этими процессами и сообщениями об их завершении. В Winsock эта проблема решена с помощью введения объектов события (event objects), по аналогии с Win32. Эти объекты создаются, уничтожаются, устанавливаются в определенное состояние и т.д. Приложение может использовать оператор WSACreateEvent для создания дескриптора (указателя) объекта события, который передается в качестве обязательного параметра для совмещаемых во времени процедур посылки и получения данных ( WSASend, WSASendTo, WSARecv, WSARecvFrom ). Каждому оператору создания объекта WSACreateEvent должен соответствовать оператор WSACloseEvent, ликвидирующий его. Объекты события используются также оператором WSAEventSelect для того, чтобы связать FD_XXX сетевое события с объектами события.

    В 32-разрядной среде операторы для работы с объектами события WSACreateEvent, WSACloseEvent, WSAResetEvent, WSASetEvent, WSAGetOverlappedResult и WSAWaitForMultipleEvents строго соответствуют операторам Win32.

    Приложение может установить режим ожидания с блокировкой для одного или нескольких объектов события, используя оператор WSAWaitForMultipleEvents. Для этих целей можно применить и WaitForMultipleObjects. Если при ожидании предпочтительнее отсутствие блокировки, можно воспользоваться оператором WSAGetOverlappedResult, чтобы проконтролировать завершение заданного процесса.

    Операторы запуска совмещаемых по времени процессов ввода/вывода WSASend, WSASendTo, WSARecv, WSARecvFrom используют в качестве опционного указателя lpCompletionRoutine, который позволяет по завершении процесса обмена передать управление определенной приложением программе.

    В версии WinSock 2 введено понятие группы сокетов, которое позволяет приложению сообщить сервис-провайдеру, что данный набор сокетов имеет определенные идентичные свойства (атрибуты). К числу этих свойств относятся относительные приоритеты отдельных сокетов в пределах группы, а также спецификация качества услуг (QOS).

    Приложения, реализующие мультимедийные потоки данных, нуждаются в организации специфических взаимоотношений между наборами сокетов. Как минимум это может включать подсказку сервиспровайдеру о приоритетности потоков информации. Например, при проведении видеоконференций звуковое сопровождение должно иметь более высокий приоритет, чем видеоинформация. Кроме того, существуют сервиспровайдеры, которые могут обеспечить запрашиваемое качество обслуживания (код QOS).

    WSASocket и WSAAccept представляют собой два новых оператора, используемых для создания сокетов и групп, а также для включения сокета в определенную группу. Идентификатор группы сокета можно узнать с помощью оператора getsockopt с опцией SO_GROUP_ID. Установка и проверка относительного приоритета сокетов в группе осуществляется соответственно операторами getsockopt и setsockopt с опцией SO_GROUP_PRIORITY. Опции сокетов приведены в таблице 4.8.

    Опции сокетов
    опция Тип назначение значение по умолчанию
    SO_GROUP_ ID GROUP Идентификатор группы, к которой принадлежит сокет NULL
    SO_GROUP_ PRIORITY int Относительный приоритет сокетов, принадлежащих группе 0
    SO_MAX_ MSG SIZE int Максимальный размер сообщения для сокетов, ориентированных на сообщения. Не имеет смысла для сокетов типа stream Зависит от реализации
    SO_PROTOCOL_INFO struct WSAPROTOCOL_INFO Описание протокольной информации Зависит от протокола
    PVD CONFIG char FAR * Информационная структура, содержащая данные о сервис-провайдере Зависит от реализации

    Сводная таблица кодов операций для процедуры ioctl приведена ниже (таблица 4.9). Оператор WSAIoctl поддерживает также все операции, специфицированные для процедуры iocltsocket.

    Коды операций
    код операции входной тип выходной тип значение
    SIO_ASSOCIATE_HANDLE зависит от API не использ. Связывает сокет с заданным дескриптором интерфейса-партнера
    SIO_ENABLE_CIRCULAR_QUEUEING не использ. не использ. Разрешает организацию кольцевой очереди
    SIO_FIND_ ROUTE struct sockaddr не использ. Запрос маршрута до заданного адреса
    SIOFLUSH не использ. не использ. Аннулирует текущее содержимое очереди на отправку
    SIO_GET_BROADCAST_ADDRESS не использ. struct sockaddr Определяет протокольно-зависимый широковещательный адрес для использования в sendto/ WSASendto
    SIOGET_QOS не использ. QOS Определяет текущую спецификацию сокета
    SIO_GET_ GROUP_QOS не использ. QOS Определяет спецификацию группы, к которой принадлежит сокет
    SIO_MULTIPOINT LOO КВАСК BOOL не использ. Определяет, будут ли данные, посланные в ходе многоточечной сессии, получены сокетом локальной ЭВМ
    SIO_MULTICAST_SCOPE int не использ. Определяет режим, в котором будут осуществляться мультикастинг-обмены
    SIOSET_QOS QOS не использ. Устанавливает новую спецификацию для сокета
    SIO_SET_ GROUP_QOS QOS не использ. Устанавливает новую спецификацию группы, к которой принадлежит сокет
    SIO_TRANSLATE_HANDLE int зависит от API Возвращает дескриптор для сокета s, который соответствует контексту интерфейса-партнера

    Оператор WSAAccept устанавливает условное соединение и имеет следующую структуру параметров (таблица 4.9.1).

    S дескриптор сокета, который находится в режиме listen
    addr опционный указатель на буфер (структуру), где должен храниться адрес подключаемого объекта. Формат адреса определяется типом протокола, заданным при создании сокета
    addrlen Опционный указатель на целую переменную, которая определяет длину аргумента addr
    IpfnCondition Адрес опционной условной процедуры, которая на основе полученной информации создает группу или подключает сокет к уже существующей группе
    dwCallbackData Параметр, возвращаемый приложению. Этот параметр не интерпретируется WinSock

    Программа извлекает очередную заявку на соединение из очереди сокета s и проверяет с помощью специфицированной программы выполнение условий соединения. Если условия выполнены, возвращается флаг CF_ACCEPT, программа создает новый сокет и осуществляет подключение его к группе в соответствии с параметром g, выработанным программой проверки условий. Вновь созданный сокет имеет такие же параметры, что и s, включая те, что задаются операторами контроля WSAAsyncSelect или WSAEventSelec t. Если программа проверки условия вернула флаг CF_REJECT, запрос на соединение аннулируется. Если невозможно принять решение немедленно, программа проверки условия должна вернуть флаг CF_DEFER, при этом никаких действий не предпринимается. Когда приложение будет готово обслужить запрос на соединение, оно снова запустит процедуру WSAAccept и пришлет либо CF_ACCEPT, либо CF_REJECT в качестве результата проверки условий.

    Для сокетов, которые остаются в блокирующем режиме, когда в очереди нет запросов на соединение, WSAAccept блокирует вызывающую программу до появления заявки на соединение. Для сокетов неблокирующего типа, когда очередь пуста, оператор WSAAccept вернет флаг ошибки.

    При завершении процедуры в addrlen будет записана реальная длина адреса в байтах. Если addr и (или) addrlen равны нулю, это означает, что нет никакой информации об адресе удаленного адресата. В противном случае эти параметры несут в себе реальную информацию независимо от результатов проверки условий.

  • Современные версии WinSock обеспечивают протокольно­-независимый доступ ко всем ресурсам сети, включая такие стандартные приложения, как DNS, SAP, X.500 и т.д.
  • Они позволяют реализовать несколько сессий ввода/вывода одновременно.
  • Они поддерживают любые стандартные уровни сервиса (QOS — Quality of service), осуществляют объединение сокетов по группам в соответствии с их параметрами.
  • Допускается многопротокольная широковещательная или мультикастинг­-адресация.
  • Предусматривается совместное использование сокетов несколькими процессами, условное создание сокетов и объединение их в группы.
  • Введено понятие WOSA-интерфейса (Windows Open Service Architecture), который обеспечивает связь между прикладной программой и сетевыми процедурами ОС. Этот интерфейс заметно упрощает программирование при работе с пакетами.

    Каждая процедура, распознаваемая WOSA, имеет, в свою очередь, несколько интерфейсов, ориентированных на конкретных сервис-провайдеров. Применение этих средств реализуется через DLL.

    WinSock, следуя модели WOSA, обеспечивает прикладной интерфейс для сетевого программирования API (Application Programming Interface), который организует доступ к транспортным услугам и серверу имен сервис-провайдера SPI (Service Provider Interface). SPI ориентирован на использование в рамках 32-битовой модели Microsoft Windows.

    Транспортные сервис-провайдеры (например, TCP/IP или IPX/SPX) и доменные серверы (DNS) в WinSock представляют собой динамические библиотеки (DLL) с одной точкой входа для процедур инициализации WSPStartup или NSPStartup (обратите внимание на то, что здесь под сервис-провайдером подразумевается система, способная предоставить определенный вид услуг). Все остальные процедуры сервис-провайдера сделаны доступными для WinSock DLL через диспетчерскую таблицу. Динамическая библиотека сервис-провайдера загружается в память WinSock DLL только по мере необходимости и выгружается, когда она более не нужна. Динамическая библиотека сервис-провайдера имеет обычно расширение .WSP или .NSP. Процедуры WinSock SPI имеют префиксы (таблица 4.9.2.).

    Сервис-­провайдеры WinSock при работе со строками применяют UNICODE. WinSock DLL выполняют необходимые преобразования при работе с приложениями, использующими ANSI или UNICODE.

    WSP WinSock Service Provider для транспортных услуг сервис-провайдера
    WPU WinSock Provider Upcall для входа в WinSock DLL сервис-провайдера
    WSC WinSock Configuration для входа в WinSock DLL инсталляционных приложений
    NSP Name Space Provider для работы с сервером имен

    Конкретный сервис-провайдер может поддерживать один или более протоколов. Так, TCP/IP-провайдер должен как минимум поддерживать TCP и UDP-протоколы. Каждый поддерживаемый протокол описывается в структуре WSAPROTOCOL_INFO, а набор таких структур представляет собой каталог используемых протоколов (более детальную информацию по данной тематике можно найти по адресу: http://www.ictp.trieste.it/~radionet/nuc1996/ref/winsock/wsspi22.doc).

    Одной из главных задач WinSock DLL является выполнение функции регулировщика информационных потоков между приложениями и сервиспровайдерами. Каждый сервиспровайдер взаимодействует только с WinSock DLL. WinSock DLL заботится об объединении потоков событий от разных сервиспровайдеров и направлении их приложению. Эта библиотека берет на себя функции арбитража и синхронизации. Взаимоотношения между сервиспровайдерами (даже если они поддерживают разные протоколы) улаживаются также WinSock DLL.

    Работа WinSock DLL базируется на параметрах сокетов, которые задаются при формировании ( socket и WSASocket ), именно эта информация определяет, какой из сервис-провайдеров будет задействован. Для выбора сервис-провайдера применяется процедура WSPSocket. В случае процедуры socket, WinSock DLL находит запись в структуре WSAPROTOCOL_INFO, которая соответствует входным параметрам (идентификатор стека протоколов, тип сокета, протокол).

    Сервис­-провайдеры используют сетевые протоколы низкого уровня. WinSock DLL осуществляет управление на среднем уровне, обеспечивая связь транспортных протоколов с приложениями.

    Транспортный SPI WinSock аналогичен WinSock API с точки зрения базовых процедур с сокетами. Так, процедурам connect и WSAConnect ставится в соответствие WSPConnect ; accept и WSAAcceptWSPAccept, а socket и WSASocketWSPSocket.

    Так как WinSock DLL не поставляется теперь разработчиками стека протоколов, расширение функциональных возможностей представляет определенную проблему. Для преодоления этих трудностей новейшие версии WinSock используют процедуру WSAIoctl, чтобы помочь сервис-провайдерам на практике реализовать какие-­либо функциональные новшества.

    Для введения расширенных возможностей приложение должно запросить указатель на точку входа в соответствующую программу. Это делается с помощью процедуры WSAIoctl, используя команду SIO_GET_EXTENSION_FUNCTION_ POINTER. При этом входной буфер процедуры WSAIoctl содержит идентификатор запрашиваемой функции, в выходной буфер будет занесен указатель на нужную программу.

    Идентификаторы, присвоенные новым функциям, должны быть уникальными глобальными идентификаторами GUID (Global Unique Identifiers), которые присваиваются им поставщиками сервис-провайдеров.

    Для того, чтобы транспортный протокол стал доступен через WinSock, он должен быть правильно инсталлирован и зарегистрирован в WinSock. Когда транспортный сервис-провайдер инсталлирован с помощью программы поставщика, соответствующая информация должна быть введена в конфигурационную базу данных, чтобы дать WinSock DLL необходимые данные для взаимодействия с сервис-провайдером.

    В WinSock DLL содержится процедура WSCInstallProvider для инсталляции программ поставщиков и процедура выгрузки этих программ WSCDeinstallProvider.

    Структура WSAPROTOCOL_INFO поставляется для каждого протокола и указывает на то, является ли этот протокол базовым, слоевым или стеком. Величина поля ProtocolChain.ChainLen интерпретируется таким образом (таблица 4.9.3.).

    0 протокол слоя базовый протокол (или стек, если стек состоит из одного протокола)
    >1 стек протоколов.

    Инсталляция стека протоколов возможна лишь после загрузки всех составных частей (слоевых и базовых протоколов). Структура WSAPROTOCOL_INFO для стека протоколов использует поле ProtocolChain для описания длины стека и идентификации каждой из составных частей. Отдельные протоколы, входящие в стек, последовательно перечислены в массиве ProtocolChain.ChainEntries, нулевой элемент списка соответствует первому протоколу слоя.

    SDK (System Development Kit) для WinSock включает в себя инструментальную WinSock и отладочную DLL.

    При разработке протокольно-­независимых приложений для систем клиент-сервер нужно зарегистрировать имя сервера в банке имен: только это может сделать его доступным извне. Программа-клиент может функционировать лишь при условии, если она способна найти необходимую ей процедуру в поле имен и получить доступ к соответствующему транспортному протоколу и адресной информации. Для тех, кто работает с протоколами TCP/IP, это может в начале вызвать определенные трудности, которые компенсируются возможностями создания программ, пригодных для широкого класса самых разнообразных протоколов. Под работой с полем имен здесь подразумевается возможность установления соответствия между протоколом и адресным атрибутом сетевой услуги, которой присвоено какое-­то имя. Примерами таких полей имен могут служить уже используемые системы DNS (Domain Name System), NDS (Netware Directory Services), X.500 и др. Поля имен могут быть динамическими, статическими и постоянными.

    Динамические поля имен служат для краткосрочной регистрации сетевой услуги. Часто они базируются на широковещательной системе определения доступности той или иной услуги. Примерами таких систем являются SAP в среде Netware и NBP в среде Appletalk.

    Статические поля имен формируются при первоначальном создании сервера имен и в дальнейшем могут изменяться лишь администратором сети. Традиционная система DNS относится именно к этой разновидности. Программа может пользоваться таким банком имен, но не может произвести туда запись.

    Постоянное поле имен сходно с динамическим — запись туда происходит в реальном масштабе времени, но после регистрации имя записывается в постоянную память и остается там до тех пор, пока не придет запрос на ликвидацию этой записи. Примерами такой системы могут служить X.500 и NDS.

    Некоторые поля имен организованы иерархически. X.500 и NDS, например, позволяют неограниченное число уровней вложения. Программные интерфейсы, предназначенные для посылки запросов в именные базы данных, сильно варьируются в зависимости от разновидности используемого поля имен. Сервер имен представляет собой резидентную программу, которая обеспечивает интерфейс между WinSock SPI и некоторой существующей базой данных имен.

    Используемые услуги в WinSock группируются в классы услуг, и в пределах класса имя услуги должно быть уникальным.

    Примерами классов услуг могут служить FTP и SQL. Каждому классу присваивается имя и идентификатор (ID). Имя класса может не быть уникальным, но идентификатор обязан быть неповторимым. В качестве идентификаторов классов услуг в Winsock применяются GUID (Globally Unique Identifiers). Для генерации GUID имеется специальная программа (UUIDGEN.EXE), которой может воспользоваться разработчик новых классов услуг.

    DNS в Internet не имеет развитой системы для записи информации о классах услуг. Для TCP/IP-класса услуг GUID присвоен раз и навсегда.

    WinSock DLL может маршрутизовать прикладные операции в пространстве имен и переадресовывать их соответствующим серверам имен.

    Процедуры установки классов услуг, регистрации и обслуживания запросов осуществляются непосредственно через интерфейс APISPI. Процедура WSAGetServiceClassNameByServiceClassId не имеет аналога среди операторов SPI, так как эта процедура WinSock DLL осуществляет обращение к NSPGetServiceClassInfo.

    Переключение сервера имен из активного состояния в пассивное и обратно осуществляется с помощью процедуры WSCEnableNSProvider. В активном состоянии могут находиться несколько серверов имен одновременно. Инициализация сервис-провайдеров выполняется с помощью процедуры WSPStartup (см. аналогично WSAStartup ). Каждой процедуре WSPStartup должна соответствовать процедура WSPCleanup.

    Процедура WSPSocket имеет параметр flags, который позволяет контролировать атрибуты сокета. Флаг WSA_FLAG_OVERLAPPED указывает на то, что данный сокет будет использоваться в режиме совмещения процессов ввода/вывода.

    Существуют атрибуты, ответственные за использование сокета для работы в многоточечном и/или мультикастинг режимах. Только сокеты, созданные для работы в многоточечном режиме, пригодны для запуска многоточечных сессий с помощью WSPJoinLeaf.

    Сокет, созданный для работы в блокирующем режиме, может быть преобразован в неблокирующий с помощью процедур WSPAsyncSelect, WSPEventSelect или WSPIoctl. Перевод сокета в блокирующий режим производится посредством процедур WSPIoctl, если WSPAsyncSelect неактивен, или WSPEventSelect — если активен.

    Процедура WSPCloseSocket ликвидирует дескриптор сокета, и все процессы, использующие этот сокет, будут прерваны.

    Понятие блокировки для среды Windows было фундаментальным. В WinSock 1.1, блокирующие процедуры WinSock были проблемой, так как они парализовали взаимодействие приложения и надстройки Windows, а применение псевдо-блокирующей техники не всегда давало удовлетворительный эффект (вспомните "голубой экран смерти" в Windows95!). В ОС типа Windows NT блокирующие процедуры уже не могут вызвать каких-­либо проблем, более того, они уже представляются привлекательными. Интерфейс WinSock 2 API не поддерживает более псевдо-блокировку, но для обеспечения совместимости с WinSock 1.1 он эмулирует этот механизм.

    В среде Win16, где настоящее блокирование не поддерживается ОС, блокирующие процедуры, которые не могут быть закончены немедленно, обслуживаются с использованием псевдо-блокировки. Сервис­-провайдер инициализирует процедуру, после чего входит в цикл, внутри которого осуществляет доставку любых сообщений Windows и проверяет завершение процедуры. Если процедура завершилась или если вызван оператор WSPCancelBlockingCall, происходит выход из цикла, а блокирующая процедура завершается с соответствующим результатом.

    Эта схема вполне приемлема для простых приложений. Но она неприемлема для приложений, где должны реализоваться сложные схемы доставки сообщений, например, для модели MDI (Multiple Document Interface). Для таких приложений псевдо-блокировка должна быть реализована в самом приложении. Сервис­-провайдер в этом случае должен вызывать именно этот цикл. Для этого он должен получить указатель на него, обратившись к WPUQueryBlockingCallback.

    Сервис-­провайдер WinSock не может исходить из предположения, что псевдо-блокировка, используемая приложением, обеспечит обработку сообщений. Если приложение не приспособлено для решения таких задач, оно откликнется флагом FALSE. Если сервис-провайдер требует обработки сообщений для своих внутренних нужд, он может вызвать процедуру PeekMessage до передачи управления циклу псевдо-блокировки в приложении.

    Сервис­-провайдер WinSock использует псевдо-блокировку лишь при выполнении определенных условий:

  • программа определена как блокирующая;
  • соответствующий сокет работает в блокирующем режиме;
  • запрос не может быть выполнен немедленно.
  • Если условия не выполнены, уход в цикл псевдоблокировки осуществлен не будет.

    Если во время цикла псевдо-блокировки получено сообщение Windows, существует опасность, что будет предпринята попытка вызова еще одной процедуры WinSock. Из­-за трудностей управления этим процессом WinSock 1.1 запрещает такие вызовы. Любая попытка осуществить вложенный вызов процедур WinSock вызовет ошибку WSAEINPROGRESS. В WinSock 1.1 это ограничение справедливо как для блокирующих, так и для неблокирующих процедур. Имеется два исключения из этого правила: процедура, которая позволяет приложению проверить, находится ли система в цикле псевдо-блокировки ( WSAIsBlocking ), и процедура ухода из этого цикла ( WSPCancelBlockingCall ).

    WinSock 2 DLL имеет средства для формирования объектов событий, как для приложения, так и для сервис-провайдера, хотя на практике объекты событий создаются в основном приложениями. Для формирования объектов событий в WinSock имеется оператор WPUCreateEvent. Дескриптор объекта события имеет смысл лишь в контексте программы, его сформировавшей. В среде Win32 работа с объектами событий выполняется самой операционной системой.

    Объекты событий в WinSock представляют собой простые конструкции, которые могут создаваться и уничтожаться, они могут устанавливаться и сбрасываться. Клиент создает объект события и передает его дескриптор в качестве параметра таким процедурам как WSPSend и WSPEventSelect. Когда оговоренные условия выполнены, сервиспровайдер использует дескриптор для того, чтобы установить объект события с помощью оператора WPUSetEvent. При этом клиент WinSock SPI может находиться в состоянии блокировки или в режиме запроса, ожидая, когда объект события будет установлен. Клиент может сбросить объект события в нуль, снова его установить и снова использовать.

    Субъект (приложение или сервиспровайдер), создавший объект события, ответственен и за его ликвидацию. Сервис­провайдер может это сделать с помощью WPUCloseEvent.

    Одной из главных задач сервиспровайдера является сообщение приложению о том, что произошло соответствующее сетевое событие. Список сетевых событий выглядит таким образом (таблица 4.9.4.).

    FD_CONNECT Канал до удаленной ЭВМ или для мультикастинг-сессии сформирован
    FD_ACCEPT Удаленная ЭВМ выставила запрос на соединение
    FD_READ Получены данные и их можно считать
    FD_WRITE В буферах сервис-провайдера появилось свободное место и можно послать очередную порцию информации
    FD_OOB Для чтения доступна высокоприоритетная информация
    FD_CLOSE Удаленная ЭВМ закрывает канал
    FD_QOS Произошло изменение оговоренного значения QOS (качества услуг)
    FD_GROOUP_QOS Произошло изменение оговоренного значения QOS для данной группы сокетов

    Стандартный BSD-интерфейс сокетов имеет только одно средство получить информацию о сетевых событиях — оператор select. Этот метод не может дать информацию о событиях FD_QOS и FD_GROUP_QOS.

    В Windows Sockets 1.1 используется асинхронный механизм получения информации о сетевых событиях. Для регистрации интересующих событий можно использовать процедуру WSPAsyncSelect. Когда нужное сетевое событие произойдет, соответствующему окну будет послано сообщение, заданное клиентом. Сервис­-провайдер использует для тех же целей процедуру WPUPostMessage. В среде Win32 этот метод получения данных о событиях нельзя считать эффективным.

    WSPEventSelect ведет себя практически так же, как WSPAsyncSelect, за исключением того, что вместо посылки сообщения Windows при сетевом событии типа FD_XXX устанавливается соответствующий объект события.

    Факт прихода сетевого события FD_XXX сервис-провайдером запоминается. Вызов процедуры WSPEnumNetworkEvents вызывает копирование текущего содержимого памяти сетевых событий в буфер клиента, а основная память событий очищается.

    Сервис­-провайдеры (ISDN или ATM) могут использовать групповые значения QOS при формировании виртуального канала и повышения эффективности своей работы. Пакеты для сокетов в пределах группы мультиплексируются обычным образом.

    Операторы WSPSocket и WSPAccept предназначены для формирования и подключения новых сокетов к той или иной группе. Однажды включенный в группу сокет остается в ней до своего закрытия. Группа прекращает свое существование, лишь когда последний сокет группы будет закрыт. Идентификатор группы сокетов может быть получен с помощью оператора WSPGetSockOpt с опцией SO_GROUP_ID. Узнать об относительном приоритете сокета в группе можно, воспользовавшись процедурой WSPGet/SetSockOpt с опцией SO_GROUP_PRIORITY.

    Групповое значение QOS можно задать при выполнении WSPConnect, или WSPIoctl с SIO_SET_GROUP_QOS, если специфицированный сокет является учредителем группы. Оператор WSPIoctl с SIO_GET_GROUP_QOS может использоваться для получения группового значения QOS заданного сокета.

    Для провайдеров, поддерживающих группирование сокетов, минимально необходимы следующие функции:

  • создание новых групп и присвоение им уникальных идентификаторов;
  • возможность включения сокета в одну из существующих групп;
  • учет провайдером значения QOS порождающего сокета при формировании списка параметров нового сокета, включаемого в группу.
  • Существуют группы двух видов. Одни включают в себя сокеты, ориентированные на соединение, они могут быть подключены только к определенному адресу ЭВМ, остальные группы относятся к другому виду. Провайдер не может включить в группу сокет, который не удовлетворяет этому условию. Провайдер не может также выполнить соединение, если адресат не соответствует адресу места назначения группы сокетов. Групповой адрес места назначения определяется, когда выполняется процедура connect для первого из сокетов группы.

    Единственный индивидуальный параметр сокета в группе — его внутренний уровень приоритета.

    Провайдер должен уметь считывать значение приоритета, но он может полностью игнорировать этот параметр.

    В настоящее время не существует каких-­либо механизмов для сопоставления приоритетов сокетов, принадлежащих к разным группам, или сокетов вне групп.

    Поддержка провайдеров групп не означает непременную поддержку различного качества услуг (QOS, см. RFC-1363).

    Поддержание работы с приоритетами сокетов в пределах группы является рекомендательным требованием. Схема приоритетов WinSock 2 имеет смысл лишь для мультиплексирования данных при соединении точка­-точка, например, в случае телефонной связи или любых других вариантов, реализующих коммутацию каналов.

    Характер информационного обмена описывается спецификацией потока (flow specs), для каждого сокета используется две такие спецификации, по одной для каждого из направлений обмена. Запрос на соединение реализуется через процедуры WSPConnect или WSPIoctl с кодом команды SIO_SET_QOS/SIO_SET_GROUP_QOS. Спецификация потока параметрически задает уровень сервиса (QOS) и определяет механизм адаптации приложения к сетевым условиям. В WinSock 2 спецификация потока содержит следующие характеристики QOS.

  • Описание источника информационного потока, которое характеризует способ, используемый приложением для введения потока данных в сеть. Эта спецификация выполняется в терминах модели маркерного ведра (token bucket) и включает частоту генерации маркеров, их размер, а также максимальное значение информационного потока.
  • Задержка (latency) — приемлемые верхние пределы задержки и ее вариации.
  • Гарантированный уровень обслуживания. Предполагается, что провайдер, который не способен обеспечить требуемый уровень сервиса (QOS), не сможет быть подключен.
  • Цена (параметр зарезервирован на будущее).
  • Параметры, специфические для провайдера (спецификация потока может быть расширена, чтобы имелась возможность описать некоторые особенности провайдера).
  • Для протоколов, ориентированных на соединение, более удобно для приложения оговаривать QOS при формировании соединения. Это делается путем запроса WSPConnect, направленного сервис-провайдеру. Если QOS было задано с помощью WSPIoctl, его значение может быть переписано при выполнении процедуры WSPConnect.

    Бессвязные сокеты могут также использовать WSPConnect с целью установления определенного уровня QOS для канала связи с партнером

    После каждой попытки установить связь провайдер производит коррекцию спецификации потока для того, чтобы наилучшим образом отразить реальную ситуацию в сети. Предполагается, что клиенты могут использовать текущую сетевую информацию, чтобы оптимально реализовать возможности сети.

    Но после того как информация о состоянии сети получена, условия могут измениться, партнеры могут согласовать другой уровень QOS, так что приложение должно быть готово ко всему. Для информирования клиента о возможных изменениях условий в Winsock используется механизм сетевых событий ( FD_QOS и FD_GROUP_QOS ). Сервис-­провайдер должен генерировать события типа FD_QOS/FD_GROUP_QOS, если уровень сервиса изменился значительно. Клиент должен использовать WSPIoctl с кодами команд SIO_GET_QOS и/или SIO_GET_GROUP_QOS, чтобы получить соответствующую спецификацию потока и выяснить, изменился ли уровень сервиса (QOS). Структура QOS должна актуализоваться вне зависимости от типа события FD_QOS/FD_GROUP_QOS. Если новый уровень сервиса неприемлем, клиент может попытаться приспособиться к новым условиям или закрыть соединение. При повторной попытке согласовать уровень QOS успешный выход из процедуры WSPIoctl указывает, что новое значение QOS приемлемо. Структура QOS в WinSock 2 описана в файле Winsock2.h.

    Приложения, которые не склонны иметь дело с QOS-структурой непосредственно, могут воспользоваться именами известных видов сервиса и получить нужные данные с помощью запроса WSPGetQOSByName.

    Прежде чем использовать сокет, надо связать его с локальным адресом. Это выполняется с помощью процедуры WSPBind, или WSPConnect.

    Сервер сначала создает сокет, связывает его с известным локальным адресом (что позволяет клиенту найти его) и переводит сокет в режим ожидания посредством WSPListen, готовя его к приему запросов на соединение. Одновременно система подготавливает структуру для формирования очереди запросов. Сервис-­провайдер заносит поступающие запросы в очередь, где они ожидают обработки. Запросы могут быть удалены клиентом из очереди по таймауту.

    Если использован сокет блокирующего типа, сервер может немедленно запустить процедуру WSPAccept, которая вызовет блокировку на время ожидания запроса на соединение. В качестве альтернативы сервер может использовать один из механизмов обработки сетевых событий, описанный ранее. В зависимости от выбранного механизма, провайдер или пошлет сообщение Windows, или даст знать о приходе запроса на соединение через объект события.

    Клиент со своей стороны создаст соответствующий сокет, запустит процедуру подключения и пошлет запрос WSPConnect, указав известный адрес сервера. Если сокет является блокирующим, WSPConnect вызовет блокировку до тех пор, пока сервер не получит и не обработает запрос на соединение или пока не произойдет таймаут. Клиент должен использовать механизм сетевых событий для отслеживания процесса соединения.

    Когда сервер запускает процедуру WSPAccept, сервис-провайдер инициализирует программу проверки условий, поставляемую приложением. В качестве параметров обращения используется информация, полученная вместе с запросом на соединение (берется из очереди запросов). Эта информация включат в себя адрес подключаемой ЭВМ, данные о пользователе и характеристику QOS, если таковая имеется. На основе полученной информации сервер определяет, принять данный запрос или нет.

    Если сервер принял запрос, он должен сформировать новый сокет с теми же атрибутами, что и сокет, ожидавший прихода запроса. Исходный сокет остается в режиме ожидания последующих запросов.

    Для получения локального адреса сопряженными сокетами используется запрос WSPGetSockName. Это особенно полезно, когда послан запрос WSPConnect без предварительного вызова процедуры WSPBind.

    Как было сказано ранее, процедура WSPAccept позволяет воспользоваться поставляемой клиентом программой проверки условий, которая осуществляет условную обработку запросов на соединения, ожидающих обслуживания. Принятие решения выполняется на основе информации, поступающей вместе с запросом (идентификатор источника запроса, QOS и т.д.). Если программа проверки условий возвращает флаг CF_ACCEPT, формируется новый сокет с теми же свойствами, что и исходный. Если программа проверки вернет флаг CF_REJECT, запрос на соединение будет отвергнут. При возвращении флага CF_DEFER принятие решения откладывается, а запрос на соединение остается в очереди. Клиент должен будет осуществить вызов WSPAccept еще раз.

    Некоторые протоколы позволяют переслать информацию в процессе установления связи. Если такая информация получена, она помещается в буфер провайдера, а WinSock SPI получает указатель на этот буфер и длину записи. Если клиент WinSock SPI желает послать некоторую информацию в ответ, он может скопировать ее в буфер, предоставляемый сервис-провайдером.

    Разрыв соединения может быть выполнен несколькими способами. Для инициализации прерывания связи можно, например, применить процедуру WSPShutdown (с параметром how равным SD_SEND или SD_BOTH ), и WSPSendDisconnect. Процедура WSPCloseSocket может быть применена и для аварийного прерывания соединения.

    Сервис-­провайдер сообщает о разрыве соединения по инициативе удаленного партнера с помощью сетевого события FD_CLOSE. При реализации процедуры разрыва соединения возможен обмен определенной информацией, если это предусмотрено используемым протоколом.

    Инициатор разрыва связи может вызвать WSPSendDisconnect, чтобы сообщить, что данных больше посылаться не будет и можно ликвидировать соединение. При этом может быть послана некоторая служебная информация, предусмотренная протоколом обмена. Для чтения этой информации партнер может воспользоваться оператором WSPRecvDisconnect. По завершении этого обмена обе стороны вызывают процедуры WSPCloseSocket.

    Следует различать процедуры прерывания ( shutdown ) связи и ее разрыва ( close ). Прерывание связи сопряжено с определенным диалогом между партнерами, после чего связь "замораживается", но дескриптор связи сохраняется. Существует два типа прерывания связи: аварийное (аппаратное) и нормальное. При нормальном прерывании любые данные, которые стояли в очереди, пересылаются до завершения процесса прерывания. При аварийном же прерывании непосланная информация теряется.

    В Windows как процедура WSPShutdown, так и WSPSendDisconnect могут использоваться для инициализации прерывания связи, в то время как запрос WSPCloseSocket служит для ликвидации дескрипторов сокетов и освобождения связанных с ними ресурсов.

    Путем установки определенных значений для опций сокетов SO_LINGER и SO_DONTLINGER можно получит следующие варианты реализации процедуры WSPCloseSocket.

  • Процедура аварийного прерывания соединения, которая возвращается из WSPCloseSocket немедленно.
  • Нормальное прерывание соединения (graceful shutdown); задержка исполнения до завершения исполнения процедуры или до истечения заданного времени. Если таймаут наступает до завершения процедуры прерывания соединения, запускается процесс аварийного разрыва соединения.
  • Нормальное прерывание соединения с немедленным возвратом и завершением процедуры прерывания в фоновом режиме. Этот алгоритм реализуется по умолчанию. Приложение при этом не знает, когда и как завершается процесс прерывания соединения.
  • Для сокетов, которые соответствуют протоколам, не ориентированным на соединение, работа, выполняемая оператором WSPConnect, связана главным образом с установлением адреса места назначения по умолчанию, что позволит в дальнейшем использовать сокет в операциях обмена, ориентированных на соединение ( WSPSend и WSPRecv ). Любые дейтограммы, полученные от отправителя с адресом, отличным от специфицированного, будут проигнорированы.

    Место назначения по умолчанию может быть изменено с помощью повторного вызова WSPConnect. Любая дейтограмма из входной очереди будет отброшена, если новый адрес отличается от адреса, заданного в предыдущем WSPConnect.

    Если новый адрес равен нулю, сокет будет отсоединен, так как удаленный адрес не определен, в результате операторы WSPSend и WSPRecv возвратят флаг ошибки WSAENOTCONN ; в то же время WSPSendTo и WSPRecvFrom могут использоваться по-прежнему. Существует три базовых способа выполнения операций ввода/вывода:

  • блокирующий ввод/вывод;
  • неблокирующий ввод/вывод с асинхронными сетевыми событиями;
  • совмещенный ввод/вывод.
  • Первый вариант является режимом по умолчанию, неблокирующий вариант может использоваться для любого сокета, который поставлен в соответствующий режим. Третья разновидность обмена реализуется только для сокетов, которые при формировании были объявлены совмещенными. Процедуры посылки WSPSend и WSPSendTo и приема WSPRecv и WSPRecvFrom реализуют все три указанных режима обмена. Сервис-­провайдеры определяют метод обмена на основе режима работы сокета, атрибутов и входных параметров.

    Простейшим режимом обмена в WinSock 2 является блокирующий ввод/вывод. Этот режим устанавливается по умолчанию. Любая операция ввода/вывода на блокирующем сокете вернет управление системе только по завершении процедуры. Таким образом, в ходе любой сессии может выполняться только одна операция ввода/вывода. Это простой режим, но отнюдь не самый эффективный.

    Если сокет находится в неблокирующем состоянии, любая операция обмена должна либо завершаться немедленно, либо возвращать флаг ошибки WSAEWOULDBLOCK, указывая, что операция не может быть завершена корректно. Необходим механизм для определения момента, когда следует попытаться выполнить операцию еще раз. Для решения этой проблемы определен список сетевых событий, наступление которых может контролироваться с помощью процедур WSPSelect, WSPAsyncSelect или WSPEventSelect.

    В WinSock 2 впервые разрешено совмещение нескольких процедур ввода/вывода и затребована поддержка этого режима всеми сервис-провайдерами. Этот режим возможен только для сокетов, сформированных с помощью WSPSocket с флагом WSA_FLAG_OVERLAPPED.

    Для приема данных клиент может воспользоваться процедурами WSPRecv или WSPRecvFrom, чтобы указать буферы, куда будут записываться данные. Если один или более буферов подготовлен приложением до начала обмена, информация будет заноситься непосредственно в буферы пользователя и многоступенчатое копирование будет исключено. Если приложением буферы заранее не подготовлены, информация заносится сервис-провайдером во внутренний буфер и система ждет запроса, чтобы перенести данные в буфер приложения. Исключением из этого правила является случай, когда приложение использует WSPSetSockOpt для установления нулевого размера входного буфера. В этом варианте данные принимаются лишь при наличии указателей на буфер приложения. При посылке информации клиенты для выдачи указателей на буфер данных используют WSPSend или WSPSendTo.

    В совмещенном режиме процедуры посылки и получения данных завершаются немедленно. Полученный по возврату нуль, указывает на то, что процедура завершилась успешно, то есть, сформирован соответствующий объект события или программа завершения установлена в очередь с помощью WPUQueueApc. Возврат флага SOCKET_ERROR, сопряженного с кодом ошибки WSA_IO_PENDING, указывает, что совмещенная операция успешно начата и позднее будет сообщено, когда выходной буфер будет свободен или входной буфер будет заполнен. Любые другие коды ошибок говорят о сбое.

    Совмещаться могут как операции ввода, так и вывода. Следует иметь в виду, что если было послано несколько блоков данных друг за другом, сообщения о завершении операций по их пересылке могут прийти в другом порядке. Тоже может произойти и при приеме данных.

    Сервис-­провайдеры имеют два способа сообщать о завершении совмещенных по времени операций: установка объекта события, заданного программой-клиентом, или запуск заданной клиентом программы завершения. В обоих случаях каждой из совмещенных операций ставится в соответствие структура WSAOVERLAPPED. Память для размещения этой структуры выделяется клиентом и используется им для указания того, какой из объектов следует установить при завершении процесса обмена. Структура WSAOVERLAPPED может использоваться сервис-провайдером для хранения дескриптора соответствующей совмещенной процедуры. Для извлечения нужной информации из указанной структуры можно воспользоваться оператором WSPGetOverlappedResult.

    Если параметр lpCompletionRoutine совмещенной операции не равен нулю, то сервис-провайдер может вызвать программу завершения, специфицированную клиентом. WinSock DLL предлагает асинхронную процедуру вызова (APC) программ завершения обмена.

    Для реализации той или иной функции в рамках сессии сервис-провайдер вызывает сначала процедуру WPUQueueApc. Сервис-­провайдер должен позаботиться о том, чтобы процесс, соответствующий выбранному контексту, был активным до вызова функции WPUQueueApc.

    WPUQueueApc берет в качестве входных параметров указатель на структуру WSATHREADID, указатель на процедуру APC и 32-разрядный контекстный код. Сервис-­провайдеры всегда получают указатель на соответствующую структуру WSATHREADID через параметр lpThreadId. Провайдер должен запомнить структуру WSATHREADID и выдать указатель на ее копию в качестве параметра оператора WPUQueueApc.

    Так как механизм APC работает только с одним 32-разрядным контекстным кодом, процедура APC не может быть сама программой завершения, заданной клиентом. Сервис­-провайдер должен выдать указатель на свою собственную процедуру APC, которая, используя контекстный код, обеспечивает доступ к необходимой информации для совмещенной операции обмена и вызывает заданную клиентом программу завершения.

    Сервис­-провайдер должен позволить клиентам WinSock 2 вызов процедур чтения или записи во время выполнения завершения обмена и гарантировать, что для данного сокета не будет допущено вложение операций завершения.

    Структура WSAOVERLAPPED создает коммуникационную среду между запуском совмещенной операции обмена и последующим ее завершением. Структура WSAOVERLAPPED совместима со структурой OVERLAPPED для Win32.

    Приоритетные данные могут доставляться пользователю независимо от обычной информации. Для коммуникационных протоколов, где предусмотрена пометка данных как срочные (например, TCP, позволяющий доставку срочной информации в общем потоке данных), система извлекает приоритетную информацию из общего потока и запоминает отдельно.

    Пользователь может определить, имеется ли приоритетная непрочитанная информация, используя процедуру WSPIoctl(SIOCATMARK). Для протоколов, где положение приоритетной информации в общем потоке имеет смысл, сервис-провайдер обеспечивает указатели, определяющие это положение. Для таких протоколов допускается обработка приоритетных данных в общем информационном потоке. Это достигается путем установки опции сокета SO_OOBINLINE (OOB OutOfBand). Для других протоколов, где блоки приоритетных данных независимы от общего информационного потока, попытка установить опцию SO_OOBINLINE вызовет ошибку. Приложение может использовать команду SIOCATMARK WSPIoctl для определения наличия непрочитанных блоков приоритетной информации.

    При запрещении SO_OOBINLINE ( disabled — значение по умолчанию):

  • сервис-провайдер WinSock сообщает клиенту о FD_OOB событиях, если клиент зарегистрирован для этого с помощью процедуры WSPAsyncSelect, точно так же FD_READ используется для сообщения о присутствии обычных данных. Таким образом, FD_OOB посылается, когда поступает приоритетная информация, а также когда данные прочитаны с использованием флага MSG_OOB, в условиях присутствия приоритетных данных, подлежащих чтению. Сообщение FD_READ для приоритетных данных не посылается;
  • сервис-провайдер WinSock возвращает соответствующий набор exceptfds при выполнении процедуры WSPSelect, если приоритетные данные присутствуют в очереди сокета;
  • клиент может вызвать WSPRecv с MSG_OOB для чтения блока приоритетных данных;
  • клиент может вызвать процедуру WSPRecv без MSG_OOB для чтения потока обычной информации. Блок приоритетных данных не может появиться в потоке обычных данных. Если приоритетные данные остаются после запроса WSPRecv, сервис-провайдер дает сообщение клиенту с помощью флага FD_OOB или через exceptfds при использовании запроса WSPSelect
  • для протоколов, где приоритетные данные находятся в потоке обычных данных, одного запроса WSPRecv недостаточно. Один вызов WSPRecv вернет обычные данные до маркера, и потребуется второй запрос WSPRecv для чтения данных после маркера.
  • Когда SO_OOBINLINE активировано ( enabled ):

  • сообщение FD_OOB не посылается для приоритетных данных, а процедуры WSPSelect и WSPAsyncSelect рассматривают эти данные как обычные, о типе информации можно судить по readfds сокета или по сообщению FD_READ, соответственно;
  • клиент не может осуществлять вызов WSPRecv с флагом MSG_OOB для чтения блока приоритетных данных — в противном случае будет получен код ошибки WSAEINVAL ;
  • клиент может вызвать WSPRecv без флага MSG_OOB. Любая приоритетная информация будет доставлена в потоке обычных данных. Приоритетные данные не будут никогда перемешаны с обычными данными, необходимо три запроса чтения для получения приоритетной информации. Первый запрос возвращает обычные данные, предшествующие приоритетным, второй — возвращает приоритетные данные, третий возвращает нормальные данные, следующие за приоритетными.
  • Программа WSPAsyncSelect прекрасно приспособлена для выявления приоритетных данных, когда SO_OOBINLINE находится в состоянии "выключено".

    Использование сокета несколькими процессами одновременно организовано следующим образом. Базовый процесс для получения специальной структуры WSAPROTOCOL_INFO вызывает WSPDuplicateSocket. Эта процедура для передачи структуры другому процессу применяет межпроцессный механизм коммуникаций (IPC). Последний процесс использует структуру WSAPROTOCOL_INFO при обращении к WSPSocket. Дескриптор сокета, полученный в результате этой операции, будет дополнительным дескриптором исходного сокета, который с этого момента может использоваться двумя процессами.

    Такой механизм разработан для того, чтобы удовлетворить как требованиям однопроцессной версии, так и многопроцессным вариантам Windows NT и XP. Следует иметь в виду, что совместное использование сокетов несколькими процессами возможно и без использования WSPDuplicateSocket, так как дескриптор сокета доступен для всех процессов.

    Когда формируется дескриптор нового сокета IFS-провайдер должен вызвать WPUModifyIFSHandle, а неIFS-провайдер должен вызвать WPUCreateSocketHandle (IFS Installable File System).

    Так как реально дублируются дескрипторы, а не сами сокеты, все параметры соединения оказываются абсолютно идентичными.

    Контроль состояния используемых совместно сокетов осуществляется посредством WSPAsyncSelect и WSPEventSelect. Вызов одного из указанных запросов с одним из дескрипторов сокета в качестве параметра аннулирует любую предшествующую регистрацию событий для указанного сокета, вне зависимости от того, какой из дескрипторов применен для новой регистрации. Таким образом, нельзя для процесса A иметь FD_READ -события, а для процесса B получать FD_WRITE -события.

    Процесс может вызвать WSPCloseSocket для дублирующего сокета и старый дескриптор будет ликвидирован, в то время как сам сокет, ему соответствующий, останется открытым вплоть до выполнения процедуры WSPCloseSocket.

    Два или более дескрипторов могут соответствовать одному и тому же сокету и использоваться независимо для операций ввода/вывода. Однако интерфейс WinSock не осуществляет какого-либо контроля за доступом, задача координации совместного использования сокета является объектом ответственности самих процессов.

    Для простоты в дальнейшем термин "многоточечный" будет означать широковещательный или мульти-кастинговый.

    В настоящее время многоточечные приложения (напр., IP-мультикастинг, STII, T.120, ATM UNI, и т.д.) значительно различаются по способу подключения узла к многоточечной сессии. Для декларации различных многоточечных атрибутов протокола в WinSock 2 используется структура WSAPROTOCOL_INFO. Просматривая эти атрибуты, программист может узнать, какие соглашения должны быть реализованы.

    В плоскости управления существует два типа различных сессий: rooted и nonrooted (корневые и некорневые). В случае корневого управления существует участник, называемый c_root, отличающийся от всех остальных членов многоточечной сессии, которые называются c_leaf (периферийные члены группы). Участник сессии c_root должен оставаться в списке участников на протяжении всей многоточечной сессии, так как без его участия сессия будет прервана. c_root обычно инициирует многоточечную сессию путем установления связей с участниками типа c_leaf. c_root может вводить членов в группу, но c_leaf может подключиться к c_root позднее.

    Для некорневой плоскости управления все участники многоточечной сессии являются периферийными узлами, и не существует какогото выделенного узла. Каждый c_leaf должен подключиться к многоточечной сессии, которая либо существует всегда (как в случае IP-мультикастинг адреса), либо создана за счет какого-то внешнего механизма. При другом подходе c_root по-прежнему существует, но принадлежит сети в целом (не является одним из участников сессии). Так как корневой узел существует, некорневая управляющая плоскость может рассматриваться как неявно корневая. Примерами такого рода неявно корневых схем являются система IP-мультикастинга — многоточечные блоки управления (Multipoint Control UnitMCU) в H.320-видеоконференциях и т.д.

    В плоскости данных также существует два стиля передачи информации: rooted и nonrooted (корневой и некорневой). В корневой плоскости данных имеется выделенный участник, называемый d_root. Обмен данными происходит исключительно между d_root и остальными участниками многоточечной сессии, которые называются d_leaf. Трафик может быть однонаправленным или двунаправленным. Данные, посланные d_root, будут доставлены всем d_leaf, в то время как данные, отправленные d_leafs, попадут только в d_root. В случае корневой плоскости данных не существует потока данных между периферийными узлами группы ( d_leaf ).

    В некорневой плоскости данных все участники эквивалентны и любая информация, посланная участником, будет доставлена всем членам группы (сессии). Аналогично, каждый узел d_leaf может получать данные ото всех остальных узлов группы, а также от любых узлов, не участвующих в данной сессии.

    В структуре WSAPROTOCOL_INFO имеется три поля атрибутов для выделения различных схем, используемых в плоскостях управления и данных.

  • XP1_SUPPORT_MULTIPOINT = 1 указывает на то, что этот протокол поддерживает многоточечные коммуникации, а последующие два поля имеют смысл.
  • XP1_MULTIPOINT_CONTROL_PLANE определяет, является ли плоскость управления корневой ( = 1 ) или не корневой ( = 0 ).
  • XP1_MULTIPOINT_DATA_PLANE указывает, является ли плоскость данных корневой ( = 1 ) или некорневой ( = 0 ).
  • В определенные моменты сокеты, включенные в многоточечную сессию, могут по своему поведению отличаться от сокетов типа точка­точка. Так, сокет вида d_leaf в корневой плоскости данных может только посылать информацию участнику d_root. Поэтому клиент должен иметь возможность заявить об этом на стадии формирования сокета. Делается это с помощью четырех многоточечных атрибутных флагов, которым присваивается определенное значение через параметр dwFlags в WSPSocket.

  • SA_FLAG_MULTIPOINT_C_ROOT служит для создания сокета, работающего как c_root. Это разрешено, если в WSAPROTOCOL_INFO указано, что существует контекст корневой плоскости управления.
  • WSA_FLAG_MULTIPOINT_C_LEAF предназначен для генерации сокета, работающего как c_leaf. Это возможно, если XP1_SUPPORT_MULTIPOINT присутствует в соответствующей записи WSAPROTOCOL_INFO.
  • WSA_FLAG_MULTIPOINT_D_ROOT предполагает формирование сокета, работающего как d_root. Это позволено, если в WSAPROTOCOL_INFO указано, что существует контекст корневой плоскости данных.
  • WSA_FLAG_MULTIPOINT_D_LEAF служит для создания сокета, работающего как d_leaf. Это допускается, если XP1_SUPPORT_MULTIPOINT присутствует в соответствующей записи WSAPROTOCOL_INFO.
  • Когда создается многоточечный сокет, один из двух флагов плоскости управления и один из двух флагов плоскости данных должны быть заданы в параметре dwFlags WSPSocket. Таким образом, при создании многоточечного сокета могут быть реализованы четыре возможности: c_root/d_root, c_root/d_leaf, c_leaf/d_root, или c_leaf/d_leaf.

    При использовании мультикастинга обычно необходимо специфицировать способ реализации сессии. Среди параметров сессии важную роль играет число вовлеченных сетевых сегментов. Для регулирования этого числа используется команда SIO_MULTICAST_SCOPE WSPIoctl. Нулевое значение числа сетевых сегментов означает, что мультикастинг-­пакеты не покинут пределы ЭВМ и могут попадать только во внутренние сокеты. Значение единица (число по умолчанию) указывает, что мультикастинг­-пакеты не могут выйти за пределы маршрутизатора локальной сети. Большие величины позволят распространение сообщений, проходящих соответствующее число маршрутизаторов. Этот код идентичен параметру времени жизни (TTL) в IP-мультикастинге.

    Многоточечный сокет часто характеризуется его ролью в плоскости данных и управления. Следует иметь в виду, что один и тот же сокет может иметь совершенно разную роль в разных плоскостях.

    В схемах для корневой плоскости периферийные узлы добавляются в многоточечную группу одним из двух способов. В первом методе корень использует WSPJoinLeaf для инициализации соединения с периферийным узлом и приглашает его быть участником сессии. Со стороны периферийного узла приложение должно создать c_leaf -сокет и использовать WSPListen для установки его в режим ожидания (listen). Периферийный узел получит сообщение FD_ACCEPT, которое означает приглашение присоединиться к многоточечной сессии, и может объявить о своем желании подключиться путем вызова WSPAccept. Корневое приложения получит сообщение FD_CONNECT, когда операция подключения к группе завершена.

    Во втором методе роли меняются. Корневой клиент создает сокет c_root и переходит в режим ожидания ( listen ). Периферийный узел, желающий присоединиться к сессии, создает сокет c_leaf и запускает WSPJoinLeaf, чтобы инициализировать подключение и доступ. Корневой клиент получает FD_ACCEPT, когда приходит запрос доступа, и принимает периферийный узел в группу посредством запроса WSPAccept. Периферийный узел получает FD_CONNECT, когда он принят в группу. Для случая IP-мультикастинга это эквивалентно опции сокета IP_ADD_MEMBERSHIP. Читателей, знакомых с использованием несвязного UDP-протокола в IP-мультикастинге, может смутить присутствующая здесь семантика, ориентированная на соединение. В частности, указание на то, что используется запрос WSPJoinLeaf для сокета UDP и ожидание сообщения FD_CONNECT могут вызвать недоразумение. Существуют уже, однако, прецеденты использования такой семантики для протоколов, не ориентированных на соединение. Разрешено и иногда полезно, например, использовать процедуру WSPConnect для UDP-сокетов. Общим результатом применения семантики, ориентированной на соединение для несвязных сокетов, является ограничение на то, как эти сокеты могут использоваться. UDP-сокет, реализуемый в WSPJoinLeaf, будет иметь определенные ограничения, а ожидание сообщения FD_CONNECT (которое в этом случае указывает на посылку соответствующего IGMP-сообщения) является одним из таких ограничений. Существует три случая, когда клиент может использовать WSPJoinLeaf.

  • При работе в качестве многоточечного сервера (root) приглашает новый периферийный узел принять участие в сессии.
  • При работе в качестве периферийного узла выполняется запрос подключения к корневой многоточечной сессии.
  • При работе в качестве периферийного узла запрашивается подключение к некорневой многоточечной сессии (например, при IP-мультикастинге).
  • Как было упомянуто ранее, WSPConnect, за исключением того, что он возвращает дескриптор сокета (как и WSPAccept ), и имеет дополнительный параметр dwFlags. Параметр dwFlags показывает, будет ли сокет работать только в режиме чтения, только в режиме записи или в обоих режимах. Только многоточечный сокет может использоваться в качестве входного параметра s этой процедуры. Если многоточечный сокет находится в неблокирующем состоянии, полученный дескриптор сокета нельзя будет использовать до тех пор, пока не будет получено сообщение FD_CONNECT. Корневое приложение в многоточечной сессии может вызвать WSPJoinLeaf один или более раз, чтобы добавить новые узлы к текущей многоточечной сессии.

    Параметры дескриптора сокета, присланного WSPJoinLeaf, варьируются в зависимости от того, является ли дескриптор c_root или c_leaf. Для c_root сокета параметр name означает имя определенного периферийного узла, который должен быть добавлен, а возвращенный дескриптор сокета является c_leaf и соответствует вновь добавленному периферийному узлу. Некоторые многоточечные приложения могут позволять этому сокету "постороннее" общение с корневым сервером.

    При вызове WSPJoinLeaf для сокета c_leaf, параметр name содержит адрес корневого приложения (для схемы корневого управления) или существующей многоточечной сессии (некорневая схема управления), и возвращенный дескриптор сокета является тождественным по отношению ко входному дескриптору сокета. В корневой схеме управления корневой клиент должен поставить свой c_root сокет в режим ожидания с помощью WSPListen. При запросе периферийного узла о присоединении к группе присылается стандартное сообщение FD_ACCEPT. Корневой клиент использует для подключения нового периферийного узла обычную процедуру WSPAccept. Запрос WSPAccept присылает дескриптор сокета c_leaf, точно так же, как и WSPJoinLeaf.

    Многоточечный корневой клиент является ответственным за прерывание сессии. Такое приложение может применить WSPShutdown или WSPClosesocket для сокета c_root, чтобы прислать всем членам группы сокетов c_leaf сообщение FD_CLOSE.

    Рассмотрим семантическое отличие многоточечных и обычных сокетов. В плоскости управления имеется существенное семантическое отличие между сокетами c_root и обычными сокетами точка­-точка.

  • Сокет c_root может использоваться в процедуре WSPJoinLeaf для подключения новых периферийных узлов.
  • Постановка сокета c_root в режим ожидания (путем вызова WSPListen ) не препятствует тому, чтобы сокет c_root применялся оператором WSPJoinLeaf для добавления в список участников нового периферийного узла или для посылки или получения информации.
  • Закрытие сокета c_root вызовет отправку всем сопряженным сокетам c_leaf сообщения FD_CLOSE.
  • Не существует какого-либо семантического различия между сокетом c_leaf и традиционным сокетом в плоскости управления, за исключением того, что сокет c_leaf может использоваться процедурой WSPJoinLeaf, а использование сокета c_leaf в WSPListen указывает на то, что должны восприниматься только запросы многоточечного соединения.

    В плоскости данных семантическое отличие между сокетами d_root и традиционными сокетами заключается в следующем:

  • данные, посланные на сокет d_root, будут доставлены всем членам группы узлов, участвующих в многоточечном обмене;
  • данные, полученные сокетом d_root, могут поступить от любого участника многоточечного обмена.
  • Сокет d_leaf в корневой плоскости данных не имеет каких-­либо семантических отличий от традиционных сокетов, однако в некорневой плоскости данных информация, посланная на сокет d_lea f, поступит ко всем периферийным узлам группы. Данные могут передаваться любым участником многоточечной сессии. Информация о том, находится ли сокет d_leaf в корневой или некорневой плоскости данных, хранится в структуре сокета WSAPROTOCOL_INFO.

    Следует иметь в виду, что перед написанием программы, использующей технологию сокетов, нужно сначала познакомиться с описанием библиотеки, которую вы собираетесь использовать.

    Вернуться к учебному плану