ИП_Л_3 Лекция № 3. Интерфейс сокетов Windows 1. Назначение и реализация Winsock. 2. Состав библиотеки функций Winsock 3. В течение нескольких последних лет операционная среда Microsoft Windows развивалась наиболее быстрыми темпами. На сегодняшний день более 50 миллионов пользователей работают в этой среде, и ожидается скорейший переход на следующую ступень развития Windows — Windows 95. Очевидно, что популярность этой операционной системы в ближайшем будущем не собирается уменьшаться. Самый быстрорастущий сегмент Интернет состоит как раз из пользователей Windows, установленной на их персональных компьютерах. Совокупность двух этих факторов приводит нас к выводу, что потребность в программистах-разработчиках приложений Интернет для Windows-компьютеров будет расти все больше и больше, так как рынок приложений еще далек от насыщения. Эта глава целиком посвящена написанию приложений Интернет для операционной системы Windows. Для многих из вас эта глава как раз то, из-за чего была куплена вся книга. В седьмой главе вы узнали, какие системные вызовы нужно выполнять приложению Интернет, чтобы устанавливать сетевые соединения через сокеты, работающие как две конечные точки сети между двумя компьютерами. В данной главе мы познакомимся с сокетами Windows и специальным программным обеспечением (оно называется Winsock), предназначенным для доступа к ним. Прочитав главу, вы овладеете следующими ключевыми понятиями: ♦Каким образом Winsock вписывается в операционную систему Windows. ♦Чем сокеты Беркли отличаются от аналогичных в Winsock. ♦Что такое операции с блокировкой в Winsock. ♦Каким образом используются специфические для Windows функции Winsock. 1. Назначение и реализация Winsock. Происхождение Winsock Вы знаете, что в процессе сетевого соединения два компьютера или процесса обмениваются данными. Сетевые профессионалы называют каждую сторону такого соединения конечной точкой (endpoint). Сокет является выражением абстракции конечной точки сетевого соединения. В 80-х годах разработчики Калифорнийского университета в г. Беркли использовали парадигму (модель) сокетов, чтобы реализовать интерфейс прикладного программирования (API) сетей на базе TCP/IP. (Из седьмой главы вам известно, что интерфейс прикладного программирования API представляет собой набор функций, используемых программистами для написания приложений в определенной сетевой операционной среде.) Интерфейс Беркли — всего лишь одна (хотя и чаще всего используемая) реализация интерфейса прикладного программирования, основанная на модели сокетов. 1 ИП_Л_3 Сокеты Windows (часто называемые «Winsock») — также интерфейс прикладного программирования, разработанный на основе сокетов Беркли. Тогда как сокеты Беркли используются на разных операционных системах, Winsock предназначен исключительно для семейства Windows, то есть для Microsoft Windows, Windows NT и Windows 95. В состав Winsock входит множество функций из интерфейса Беркли, изначально разработанных для Unix — операционной системы, для которой и предназначались сокеты Беркли. Кроме того, имеется набор специфических для Windows функций, позволяющих программистам пользоваться преимуществами интерфейса Windows, основанного на передаче сообщений. В данной главе описываются как специфические по сравнению с сокетами Беркли функции, так и сетевое программирование в Windows само по себе. Спецификация Winsock, приведенная в данной главе, соответствует версии 1.1. Реализация Winsock Сеть Интернет основывается на протоколах TCP/IP. Windows — самая популярная среда программирования на сегодняшний день. Спецификация Windows Sockets описывает стандарт, по которому программы Windows обязаны общаться с сетями на базе TCP/IP. Таким образом, Windows Sockets API представляет колоссальные возможности в развитии старых и написании новых сетевых приложений. Корпорация Microsoft, создатель и владелец Windows, не имеет имущественных прав на стандарт Winsock. Усилия, необходимые для создания спецификации Windows Sockets, были затрачены большим количеством людей, работающих в различных корпорациях. Цель разработки — создание единого интерфейса прикладного программирования (API), который бы использовался в равной мере как разработчиками, так и продавцами сетевого программного обеспечения и служил бы стандартом работы с TCP/IP для Windows. Современный стандарт Windows Sockets (версии 1.1) обеспечивает работу исключительно сетей на базе TCP/IP, хотя положение вещей может и измениться в будущем. Мы уже говорили о том, что первоначально интерфейс сокетов был встроен в UNIX. Другими словами, API оказался частью операционной системы. Интерфейс сокетов Windows не входит в состав Windows, а реализован в виде динамически загружаемой библиотеки (DLL). Общая картина Чтобы осознать картину программирования в Windows целиком, нужно понимать, какое место в ней занимает Winsock и какие именно функции системы им выполняются. Множество фирм продают свои приложения, полезные утилиты и программы. Для того кто не знаком со структурой Winsock, бывает сложно определить, где заканчивается разработка и начинается Winsock. В главе 6 вы выяснили, что для передачи сетевой информации по модему и телефонным линиям в Интернет существуют протоколы последовательной передачи данных — SLIP и РРР. Производители продукции на базе Winsock часто .разрабатывают свои собственные версии этих протоколов. Как правило, одним из компонентов бывает программа для набора телефон2 ИП_Л_3 ного номера для Windows. Она позволяет связать компьютер под Windows с Интернет посредством SLIP или РРР. Очень часто производители сетевого программного обеспечения разрабатывают собственный стек протоколов TCP/IP, включая его в состав продукции. Несмотря на очевидную полезность (и даже, возможно, необходимость) всех этих разработок для написания собственных приложений TCP/IP, необходимо понимать, что все это само по себе не является частью интерфейса Winsock. На рис. 8.1 показано, как интерфейс Winsock вписывается в общую схему разработки TCP/IP программ для Windows. Рис. 8.1. Место, занимаемое Winsock в операционной среде Windows Модуль WINSOCK.DLL находится между стеком протоколов TCP/IP и клиентскими приложениями. Другими словами, он управляет интерфейсом к стеку TCP/IP. В предыдущей части книги вы подробно ознакомились как с протоколами TCP/IP, так и с протоколами последовательной передачи данных SLIP и РРР. Но некоторые компоненты, изображенные на рис 8.2, вам, наверное, еще незнакомы. В следующих разделах мы обсудим эти моду лидрайверы программного обеспечения вместе с протоколами связи высокого уровня. 2. Состав библиотеки функций Winsock Краткое обозрение библиотеки функций Winsock Интерфейс прикладного программирования Winsock содержит набор (библиотеку) функций общего пользования, требуемых для решения определенного класса задач. Спецификация Winsock разделяет всю библиотеку на три группы: • Функции сокетов в стиле Беркли, включенные в состав Winsock API. • Функции для работы с базами данных, позволяющие программам получать информацию об именах доменов, коммуникационных службах и протоколах. 3 ИП_Л_3 • Специфические функции Windows, расширяющие набор функций интерфейса сокетов Беркли. В следующих разделах мы коротко обсудим все три группы функций. В нашей книге мы также разделим функции библиотеки на два вида: блокирующие и не блокирующие. Блокирующая функция заставляет вызвавшую ее программу ждать окончания сетевой операции ввода-вывода. Не блокирующая функция либо завершается сразу, либо возвращает сообщение об ошибке. Другими словами, не блокирующие функции не ждут окончания операции. Блокирование — один из наиболее важных для понимания аспектов сетевого программирования. Сетевая литература слишком часто употребляет этот термин, не затрудняясь объяснить его значение. Мы обсудим блокирование более подробно немного позже в последующих разделах. Функции интерфейса сокетов Блокирующая операция задерживает выполнение программы до окончания своей работы. Как правило, все функции сетевого ввода-вывода сокетов в стиле Беркли — блокирующие. На деле, блокирование проявляется задержкой в выполнении программы до окончания передачи-приема сетевых данных. В табл. 8.1 приведены функции сокетов в стиле Беркли, которые могут блокировать выполнение операций в Winsock API. Таблица 8.1. Функции в стиле Беркли, которые могут блокировать операции Winsock API Функция accept closesocket Описание Подтверждает запрос на установление соединения. Образует новый сокет и соединяет его с удаленным сетевым компьютером, запрашивающим соединение. Исходный сокет возвращается в состояние приема входящих запросов. Закрывает одну сторону в соединении сокетов. connect recv Устанавливает соединение на указанном сокете. Принимает данные из соединенного сокета. recvfrom select send Принимает данные из соединенного или не соединенного сокета. Выполняет синхронные мультиплексные операции ввода-вывода путем наблюдения за состоянием нескольких сокетов. Передает данные через соединенный сокет. sendto Передает данные через не соединенный или соединенный сокет. Изучив описания функций сокетов, приведенные в табл. 8.1, можно заметить, что все они либо производят операции ввода-вывода, либо ждут окончания сетевого ввода-вывода до того, как завершить выполнение. Отсюда можно сделать вывод, что любая функция, так или иначе связанная с операциями ввода-вывода, может блокировать выполнение остальных функция Winsock API. С другой стороны, функции, приведенные в табл. 8.2, не выполняют операций ввода-вывода в процессе работы. Они либо преобразуют информацию, либо имеют дело с локальным сокетом сетевого компьютера. Другими словами, их деятельность не связана с удаленными сетевыми устройствами. 4 ИП_Л_3 Поэтому, несмотря на то, что они также являются функциями в стиле Беркли, ни одна из них не блокирует операции прикладной программы. Таблица 8.2. Функции в стиле Беркли, не блокирующие работу WinsockAPI Функция bind getpeername getsockname getsockopt htonl htons inet_addr inet_ntoa ioctlsocket listen ntohl Описание Присваивает имя неинициализированному (новому) сокету. Получает имя удаленного процесса, связанного с указанным сокетом. (В дальнейшем вы узнаете, что Winsock API хранит эту информацию в локальной структуре данных, поэтому вызов данной функции не связан с операциями сетевого ввода-вывода.) Возвращает имя указанного местного (локального) сокета. Возвращает статус (опции) указанного сокета. Преобразует порядок байтов в 32-разрядном числе из машиннозависимого в сетевой. Преобразует порядок байтов в 16-разрядном числе из машиннозависимого в сетевой. Преобразует строку с IP-адресом в формате десятичное с точкой в 32разрядное двоичное число (с сетевым порядком байтов). Преобразует IP-адрес в формат десятичное с точкой. Управляет параметрами сокета, относящимися к обработке операций сетевого ввода-вывода. Переводит указанный сокет в состояние прослушивания запросов на входное соединение. (Функция переводит сокет в режим прослушивания, однако сама по себе не производит никаких операций сетевого вводавывода.) Преобразует порядок байтов 32-разрядного числа из сетевого в машиннозависимый (порядок хоста). ntohs Преобразует порядок байтов 16-разрядного числа из сетевого в машиннозависимый (порядок хоста). setsockopt shutdown Устанавливает режим (опции) работы сокета. Закрывает одну сторону дуплексного соединения (только для местного компьютера). Образует точку сетевого соединения и возвращает дескриптор сокета. socket Функции для работы с базами данных Программы Интернет должны понимать различные виды адресации. Например, пользователь может указывать имя, например jamsa.com, а может — адрес в виде десятичного с точкой, например 168.158.20.102. До того как использовать эти виды адресации, программа должна преобразовать их к единому виду — структуре данных сокета. Функции Winsock API умеют работать лишь с такими структурами. Когда программа выводит данные, она должна совершить обратное преобразование: из структуры данных сокета в вид, понятный пользователю. К счастью, функции для работы с базами данных, список которых приведен в табл. 8.3, кроме всего прочего, успешно решают задачи прямого и обратного преобразования адресов. Они позволяют прикладной программе получать информацию об именах доменов, коммуникационных службах и протоколах. Для выполнения своих задач этим функ5 ИП_Л_3 циям приходится обращаться к разнообразным, как местным, так и удаленным, источникам информации. Спецификация Winsock определяет интерфейс, а также формат данных, который возвращается функциями для работы с базами данных. Вопросы хранения и выборки данных зависят от конкретной реализации Winsock и не оговариваются в стандарте. Таблица 8.3. Функции Winsock API для работы с базами данных Функция Описание gethostbyaddr Возвращает имя (имена) и IP-адрес, соответствующие указанному сетевому адресу. Возвращает имя (имена) и IP-адрес, соответствующие указанному сетевому имени. Возвращает имя локального сетевого компьютера. Возвращает официальное имя и номер протокола по указанному имени (например, TCP). (В семействе протоколов TCP/IP каждому протоколу соответствует уникальное целое число.) gethostbyname gethostname getprotobyname getprotobynumber getservbyname getservbyport Возвращает имя и номер протокола по указанному номеру. Возвращает имя сетевой службы (например, time) и номер порта протокола, соответствующие указанному имени. Возвращает имя сетевой службы и номер порта протокола, соответствующие указанному номеру порта. Некоторые из вышеописанных функций возвращают указатели на значения, хранящиеся в системной области памяти (volatile). Реализация Winsock может использовать такие области повторно при вызове следующей функции. Это значит, что, если программа желает использовать полученное значение переменной, его необходимо переписать в переменную, принадлежащую программе, до следующего вызова функций, иначе оно может оказаться утерянным. Данные, находящиеся в системных буферах, действительны только до следующего вызова функции Winsock. Интерфейс прикладного программирования Winsock включает так называемые асинхронные (специфические для Windows) аналоги всех функций для работы с базами данных, за исключением gethostname. В дальнейшем мы еще обсудим асинхронные функции Windows. На данный момент сообщим только, что асинхронные функции представляют собой Windows-расширения интерфейса сокетов Беркли. Они появились в результате стремления разработчиков использовать встроенный в Windows механизм обмена сообщениями. В табл. 8.4 перечислены семь функций в стиле Беркли вместе с асинхронными эквивалентами. Все они работают с базами данных и входят в состав Winsock API. 6 ИП_Л_3 Таблица 8.4. Асинхронные функции для работы с базами данных в составе Winsock API Функция в стиле Беркли gethostbyaddr Асинхронный эквивалент WSAAsyncGetHostByAddr gethostbyname WSAAsyncGetHostByName gethostname getprotobyname getprotobynumber getservbyname getservbyport (He имеет эквивалента) WSAAsyncGetProtoByName WSAAsyncGetProtoByNumber WSAAsyncGetServByName WSAAsyncGetServByPort Функции-расширения, специфические для Windows Как мы только что отметили, разработчики Winsock реализовали специальные асинхронные версии некоторых функций для работы с сокетами, для того чтобы программисты могли пользоваться преимуществами механизма передачи сообщений в Windows. Чем больше вам придется заниматься написанием сетевых программ для Windows, тем чаще и эффективнее вы будете употреблять асинхронные функции. В табл. 8.5 приведены все Windowsрасширения, входящие в состав Winsock API, за исключением функций для работы с базами данных, так как они уже приведены в табл. 8.4. Таблица 8.5. Описание функций-расширений Windows, за исключением асинхронных функций для работы с базами данных Функция Описание WSAAsyncSelect WSACancelAsyncRequest Асинхронный вариант функции select. Прекращает некорректное выполнение функции WSAAsyncGetXByY. Прекращает некорректное выполнение блокирующей функции API. Уведомляет Windows Sockets о том, что программа закончила работу с DLL. Возвращает сообщение о последней ошибке при вызове функции. Определяет, является ли низ лежащий уровень Winsock DLL блокирующим. Устанавливает ловушку блокирующего вызова. (См. соответствующий раздел.) Фиксирует сообщение об ошибке для последующего вызова WSAGetLastError. Инициализирует низлежащий уровень Winsock DLL. WSACancelBlockingCall WSACleanup WSAGetLastError WSAIsBlocking WSASetBlockingHook WSASetLastError WSAStartup WSAUnhookBlockingHook Восстанавливает первоначальную блокирующую функцию. Сокеты Беркли по сравнению с сокетами Winsock Модель программирования Winsock, описанная в предыдущих разделах, тесно связана с моделью сокетов Беркли, которую мы рассмотрели в седьмой главе. И это не случайно, поскольку разработка Winsock базирова7 ИП_Л_3 лась на модели интерфейса Беркли. Интерфейс сокетов Беркли наложил отпечаток на структуру сетевого программного обеспечения TCP/IP в мире UNIX. Концепция сокетов дает программистам понятную и удобную модель для разработки сетевых приложений. В главе 7 мы писали, что интерфейс Беркли дает инструмент для создания как весьма сложных серверных приложений, так и устойчиво работающих приложений-клиентов. В результате, одной из заявленных при разработке спецификации Winsock целей была следующая: «... необходимо обеспечить безболезненный переход любого, знакомого с программированием сокетов в Unix и других ОС программиста в среду Windows, а также легкость переноса туда исходных текстов уже существующего сетевого программного обеспечения.» Первая часть декларации с успехом выполнена. Если вам приходилось разрабатывать программы с сокетами в других средах, вы без особого труда перейдете к сокетам Windows. Однако вторую часть декларации оказалось не так легко выполнить. Хотя Winsock и облегчает задачу переноса исходного текста из других сред программирования, все оказывается не так просто, как хотелось бы. Вы легко начнете программировать Winsock, что называется «с нуля», особенно если вы знакомы с сокетами Беркли. Однако занявшись переносом уже готовой программы под Windows, можно встретить массу неприятностей. Первым делом, наверное, вы захотите использовать функции Winsock в стиле Беркли, избегая специфических для Windows расширений. Через некоторое время станет ясно, что без расширенных функций Windows не обойтись. При этом имейте в виду, что вследствие разницы между сокетами Беркли и сокетами Windows очень может быть, что программу придется просто переписать заново. Возможно, ее придется переписать до того, как вы начнете встраивать специфические функции Windows. Пишите ли вы программу «с нуля» или переносите уже существующую, вам всегда поможет следующее краткое обозрение различий между сокетами Windows и сокетами Беркли. Конечно, мы предполагаем, что вы знакомы с сокетами Беркли, описанными в седьмой главе. Файлы заголовков В любой сетевой программе Unix необходимо указывать массу файловзаголовков, описывающих различные сетевые функции. В программах Winsock указывается только один файл-заголовок winsock.h. Он указывается всегда (#include <winsock.h>), если приложение работает с сокетами или вызывает любую функцию, входящую в Winsock. В большинстве Windowsпрограмм требуется также указывать файл-заголовок windows.h. Если winsock.h включен, то необходимости дополнительно включать windows.h нет — это делается автоматически в файле winsock.h, поскольку он не может обойтись без windows.h. Управление данными При разработке Windows-программ необходимо учитывать следующую особенность: любой объект памяти, например буфер или переменная, должен быть доступен для WINSOCK.DLL в любой момент времени при исполнении 8 ИП_Л_3 операций Winsock. В многопоточных версиях Windows программа обязана самостоятельно координировать доступ к объектам памяти. Об этом прямо сказано в спецификации Winsock, где указано, что реализации Winsock (WINSOCK.DLL) не отвечают за управление памятью программы. Обязательные функции Winsock В Winsock определены две обязательные для исполнения функции: WSAStartup и WSACleanup. Первая вызывается перед тем, как вызвать любую другую функцию Winsock. Вторая — по окончании работы с Winsock. Для каждой функции WSAStartup всегда должна вызываться соответствующая функция WSACleanup. В следующих разделах подробно описываются действия, производимые каждой функцией. Функция WSAStartup Функция WSAStartup позволяет программе указать требуемую версию Winsock. Она также позволяет программе получить информацию о конкретной реализации Winsock. Фактически, при вызове WSAStartup происходит диалог между программой и модулем WINSOCK.DLL. Возможность узнавать версию Winsock предусмотрена для того, чтобы обеспечить разработку программ, использующих более новые и не работающих со старыми версиями Winsock. Такая программа указывает минимальную версию, с которой она еще может работать, а Winsock — наиболее высокую версию из тех, что он может обеспечить. Далее программа решает, как ей лучше поступить в том или ином случае. Спецификация Winsock содержит рисунок, демонстрирующий варианты «переговоров» разных версий DLL при вызове WSAStartup. Функция WSACleanup Прикладная программа может вызывать WSAStartup несколько раз за время работы. Это может понадобиться, например, если в разных частях программы требуется узнать версию WINSOCK.DLL. И вместо того чтобы отводить глобальный массив для хранения версии, программа вызывает WSAStartup. Функция WSACleanup органично дополняет WSAStartup. На каждый вызов функции WSAStartup должен приходиться вызов WSACleanup. Winsock регистрирует каждый вызов WSAStartup при помощи внутреннего счетчика вызовов. Каждый раз при вызове WSAStartup значение счетчика увеличивается. При вызове WSACleanup, наоборот, уменьшается. Последняя, вызванная программой функция WSACleanup (устанавливающая счетчик в ноль) заставляет Winsock произвести операции по очистке буферов, переменных и т. д., имевших отношение к программе. Реализация Winsock (WINSOCK.DLL) отводит внутренние статические области памяти для зарегистрировавшейся в ней программы. Каждая программа получает собственные области памяти, к которым можно обращаться только через Winsock API. Области памяти являются внутренними для Winsock точно так же, как и структуры данных сокета. Последняя вызванная WSACleanup говорит Winsock, что он не нужен больше программе, следовательно, можно освободить все ресурсы, отведенные ранее для этой программы. Функция WSAStartup начинает работу программы с Winsock, a WSACleanup прекращает ее. 9 ИП_Л_3 Как только WSACleanup вызвана в последний раз, Winsock отсоединяет все оставшиеся сокеты, ориентированные на соединение. Тем не менее все данные, оставшиеся в выходных очередях, отправляются. Предположим, что закрытие сокета следует сразу же за командой send. Даже если сеть и не успела передать все данные до вызова WSACleanup, они все равно будут посланы Winsock. Как мы уже говорили, на любой вызов WSAStartup должен приходиться вызов WSACleanup. Это позволяет очистить отведенные для программы области памяти в Winsock. Вызывая WSACleanup, вы говорите Winsock, что не нуждаетесь больше в его услугах и в выделенной для программы памяти. Winsock в свою очередь может освободить любую память, ранее принадлежавшую программе. Многие реализации Winsock ведут себя корректно, даже если WSACleanup не вызывалась программой. В спецификации Winsock говорится о том, что такого поведения вполне можно ожидать и, следовательно, к нему нужно подготовиться. С другой стороны, Winsock не обязан выполнять какие бы то ни было действия при некорректном поведении программы. Другими словами, то что происходит при окончании программы, которая не вызвала WSACleanup, зависит от конкретной реализации Winsock. Таким образом, программе необходимо тщательно отслеживать последовательность вызовов WSAStartup и WSACleanup. Особенно это относится к аварийному завершению программы. Даже в этом случае следует предусмотреть корректное окончание работы с WINSOCK.DLL, чтобы сбой в программе не отразился на обслуживающих ее сетевых модулях. Примечание: Если программа разрабатывается для многопоточной среды Windows 95 или Windows NT, имейте в виду, что функция WSACleanup относится ко всем потокам завершаемого процесса. Это важно, поскольку многие функции Winsock относятся исключительно к тому потоку процесса, из которого были вызваны. WSACleanup к таковым не относится — она завершает сетевые операции всех потоков, связанных с завершаемой программой. 10