Сокеты
Как было сказано ранее, процедура 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 находится в состоянии "выключено".