Internet, чья популярность необычайно возросла в последние годы
и продолжает с каждым днем увеличиваться, предлагает широчайшее
поле деятельности как для крупных компаний, занимающихся
разработкой программного обеспечения, так и для немногочисленных
групп и даже отдельных программистов. Едва ли стоит создавать
еще один броузер для Всемирной сети - этот рынок уже поделен и
не потерпит новичка, но найти свою нишу, способную приносить
доход небольшому коллективу программистов, - вполне реальная
задача. Ниже речь пойдет о том, как, используя функции
Winsocket, создавать клиентские программы под Windows для работы
со службами Internet.
Winsocket
В 80-х гг. ученые университета города Беркли (шт. Калифорния)
разработали концепцию передачи информации по сети от одного
компьютера к другому посредством универсальной модели передачи
данных, не зависящей от типа платформы и программного
обеспечения, установленного на ней. Предложенная ими модель
оказалась настолько удачной, что практически все функции,
описанные в ней, используются и в последующих реализациях, одной
из которых является Winsocket.
Трудно подобрать русский эквивалент слову "socket": в буквальном
переводе это розетка или патрон для электрической лампочки. И
действительно, изображение электрической розетки стало
классической эмблемой сокета. Такое представление вполне
отвечает его назначению - являться портом для поступающей или
отправляемой информации.
Winsocket, поставляемый со всеми последними версиями Microsoft
Windows, представляет собой набор функций, оформленных в виде
динамической библиотеки (DLL), и предназначен для осуществления
сетевого взаимодействия между компьютерами. Благодаря этому
становится возможным применение его встроенных средств в
приложениях, создаваемых независимыми разработчиками с помощью
любых языков программирования, при условии, что в них имеются
встроенные средства для использования функций из внешних
динамических библиотек. Такими инструментами могут быть,
например, Borland Delphi, Microsoft Visual Basic или различные
реализации С/C++.
Создатели Winsocket сделали его универсальным средством для
построения сетевых приложений: задуманный как интерфейс
прикладного программирования для сетей TCP/IP, он, тем не менее,
может работать с любыми сетевыми протоколами. Чтобы обеспечить
совместимость разрабатываемого программного обеспечения, а также
избежать ошибок при попытке вызова функций, отсутствующих в
устаревших версиях Winsocket, в состав библиотеки были введены
две функции - WSAStartup и WSACleanup. Функция WSAStartup, кроме
непосредственно инициализации библиотеки, позволяет узнать
версию Winsocket, установленную на данном компьютере, и
вызывается до любой другой функции из числа входящих в состав
библиотеки. Вторая функция - WSACleanup - должна вызываться
последней из функций сокета, например по окончании работы
программы. Эта функция очищает программные буферы и переменные,
которые были задействованы сокетом. Обе функции могут вызываться
в программе неоднократно, но на каждый вызов WSAStartup должен
приходиться вызов WSACleanup.
Работа с Winsocket в какой-то степени напоминает работу с
файлами или COM-портами персонального компьютера: для того чтобы
получить или передать информацию (а именно обеспечение обмена
информацией и является главной задачей Winsocket), необходимо
сначала создать сокет и получить ссылку на соответствующую
область памяти. Указанная информация потребуется в дальнейшем
для управления данным экземпляром сокета, а в конце работы - для
его закрытия и освобождения занимаемой памяти. Для этого
используется функция Socket(Семейство_Протоколов, Тип_Сокета,
Тип_Протокола). Такое количество аргументов у данной функции
связано с тем, что Winsocket - это универсальный инструмент для
осуществления сетевого взаимодействия как в небольшой локальной
сети из нескольких компьютеров (интрасеть), так и в глобальных
сетях с тысячами компьютеров, работающих под управлением разных
операционных систем (Internet). Параметр Семейство_Протоколов
указывает на группу протоколов, с помощью которых будет
осуществляться взаимодействие. Например, использование константы
PF_INET означает, что вы хотите работать с семейством протоколов
Internet, PF_UNIX - с семейством Unix-протоколов, а PF_IPX -
IPX/SPX. Константа Тип_Сокета может принимать значения
SOCK_STREAM и SOCK_DGRAM, соответствующие работе с потоком
байтов и с дейтаграммами (группами байтов). И наконец, третий
параметр указывает на протокол, по которому будет осуществляться
соединение: IPPROTO_TCP говорит о том, что будет использоваться
протокол TCP - основной протокол четвертого уровня в Internet. В
результате некоторой двусмысленности данной терминологии может
показаться, что вам придется работать с двумя протоколами
одновременно, например с TCP и FTP. Следует помнить, что TCP,
SPX и т. д. являются протоколами транспортного уровня, а FTP,
SMTP и т. п. - протоколы обмена информацией с определенными
службами, к примеру с файловой или службой отправки почтовых
сообщений. Таким образом, вызов функции создания нового сокета
для работы, скажем, с почтовыми службами (SMTP и POP3) или
протоколом передачи файлов по Internet (File Transfer Protocol)
будет выглядеть следующим образом: Socket(PF_INET, SOCK_STREAM,
IPPROTO_TCP). При этом функция вернет либо ссылку на вновь
созданный сокет, если этот процесс завершится успешно, либо
значение INVALID_SOCKET. В этом случае, чтобы получить код
ошибки, необходимо вызвать функцию WSAGetLastError.
Обратите внимание на то, что при создании сокета не указывается
ни адрес удаленного компьютера, к которому вы хотите
подключиться, ни ваш локальный адрес. Локальный адрес вам
потребуется указать лишь при работе с некоторыми протоколами
высокого уровня, например с SMTP (протокол передачи сообщений)
или NNTP (протокол работы со службами новостей). В зависимости
от типа сетевого взаимодействия для настройки сокета могут
использоваться различные функции, реализованные в библиотеке
Winsocket: если вы создаете программу-клиент, ориентированную на
соединение, достаточно вызвать функцию Connect, если же ваша
программа не предназначается для постоянного соединения, вы
должны использовать функцию Bind (см. врезку "Ориентированные и
не ориентированные на соединение программы").
Работа с Winsocket предусматривает два режима: блокирующий и
неблокирующий. Смысл этих терминов заключен в их названии: в
блокирующем режиме выполнение функций, следующих за блокирующей,
приостанавливается до ее завершения, а в неблокирующем такой
задержки не происходит. Проиллюстрировать сказанное можно
следующим примером: ваша программа в цикле опрашивает
коммуникационный порт; если его буфер пуст, программа
возвращается к началу цикла, в противном случае она считывает
данные и переходит к выполнению следующих функций. Здесь
применяется блокирующий режим. Если же ваша программа, проверив
наличие новой информации в буфере порта, должна приступить к
выполнению следующих операций вне зависимости от наличия в нем
данных, вам необходимо перейти в неблокирующий режим.
Неосторожное использование сокета в блокирующем режиме может
вызвать полное зависание программы, а в некоторых случаях,
особенно при эксплуатации программы в 16-разрядных операционных
системах, и всей ОС. Тем не менее применение более безопасных с
этой точки зрения неблокирующих вызовов требует для обеспечения
"прослушивания" сокетом своих входных портов дополнительных
действий со стороны программиста. Для того чтобы программа могла
прервать работу зависшей функции, запущенной в блокирующем
режиме, был введен параметр "максимальное время ожидания ответа"
(timeout). Он не является внутренней переменной Winsocket и
должен быть реализован внешними по отношению к библиотеке
функциями. Смысл введения этого параметра в том, чтобы прервать
работу функции, заблокировавшей выполнение программы, по
окончании указанного промежутка времени, предоставив тем самым
пользователю решать, повторить ли последнюю операцию или
выполнить иные действия.
Получив подтверждение от удаленного сервера, ваша программа
может приступать к обмену данными. Для этого в библиотеке
Winsocket предусмотрены функции передачи данных (Send, Sendto) и
чтения данных (Recv, Recvfrom). В случае успешно проведенной
операции эти функции возвращают число переданных или принятых
байтов, в противном случае возвращаемое значение равно константе
SOCKET_ERROR и для получения кода ошибки вам потребуется вызвать
функцию WSAGetLastError.
Информация на удаленный сервер должна передаваться, как
указывалось выше, в соответствии с вполне определенным
протоколом высокого уровня. Обычно программа-сервер,
функционирующая на удаленном компьютере, поддерживает строго
оговоренный соответствующим протоколом набор команд. Каждая из
них передается ей в виде строки символов ASCII, представляющей
собой команду и аргумент, если он необходим. Такое соглашение
было принято для того, чтобы сделать протоколы
аппаратно-независимыми, поскольку Internet - сеть гетерогенная.
Так, в состав протокола SMTP (RFC 821) для передачи электронной
почты входят следующие команды: HELO - инициализирует приемник и
сообщает ему информацию о передатчике, MAIL - сообщает о начале
почтовой транзакции, RCPT (от англ. recipient) - называет
серверу адрес получателя сообщения, DATA - указывает, что
следующие за этой командой строки являются телом сообщения, т.
е. полезной информацией. После передачи сообщения требуется
передать команду QUIT. Полное описание этого и других популярных
протоколов можно получить с общедоступных серверов по адресам,
приведенным в табл. 1. Познакомиться с подробным описанием
функций Winsocket в формате файла справочной системы Windows, а
также получить комплект интерфейсных модулей для использования
Winsocket, содержащих описание функций и переменных для
программирования на C, Basic или Pascal, можно, подключившись к
анонимному FTP-серверу
ftp://ftp.stardust.com/pub/winsock/version1/dev/. Пользователи
Borland Delphi получают аналогичный модуль и справочный файл в
комплекте поставки.
Использование готовых компонентов
Всегда ли при создании Internet-приложений стоит начинать с
самого "низа", т. е. с программирования сокетов? За видимой
простотой этого занятия скрывается множество сложностей. Не
следует забывать, что, кроме создания и управления сокетом, вам
придется обеспечивать поддержку протокола высокого уровня, а
возможно, и не одного, что тоже может отнять массу времени. Так,
например, при создании приложения для работы с электронной
почтой программисту необходимо знать, как минимум, три
протокола: SMTP - для передачи сообщений, POP3 - для приема
сообщений и MIME - для кодирования и декодирования
присоединенных файлов. Быть может, есть смысл часть рутинной
работы переложить на выпущенные независимыми фирмами программные
компоненты? Их, к счастью, в настоящее время создано немало, но
следует иметь в виду, что большинство из них - отнюдь не
бесплатные продукты, хотя часть фирм и предоставляет пробные
версии компонентов через свои общедоступные серверы во
"Всемирной паутине".
Многие предлагаемые в Internet компоненты выполнены в формате
ActiveX - самом распространенном на сегодняшний день.
Преимущества этого типа компонентов перед запатентованными
форматами компонентов ряда других сред разработки заключаются в
том, что поддержка ActiveX включена, как правило, во все
подобные продукты. К тому же эти компоненты разрабатываются
фирмами не первый год и обычно обладают хорошо отлаженным кодом
и продуманной справочной системой. Основным же недостатком
является одноплатформность, что, впрочем, свойственно
компонентному подходу вообще.
Если вы пользователь Borland Delphi версии 2.01, то вместе со
средой разработки получите комплект компонентов ActiveX фирмы
NetManage (http://www.netmanage.com). Обладателям более старых
версий Delphi или других средств разработки придется приобретать
такие комплекты дополнительно. Список наиболее популярных
производителей компонентов приведен в табл. 2.
Win32 Internet API
Еще один подход в создании Internet-приложений, являющийся
альтернативой как самостоятельному программированию функций
Winsocket, так и использованию готовых компонентов независимых
производителей, может заключаться в применении Microsoft Win32
Internet (WinInet) API. Этот интерфейс также реализован в виде
динамически компонуемой библиотеки (wininet.dll) и в настоящее
время входит в комплект поставки Microsoft Internet Explorer, а
впоследствии войдет и в состав операционных систем корпорации
Microsoft. Сегодня эта библиотека включает в себя поддержку
клиентских протоколов FTP, HTTP и Gopher.
Функции, входящие в Win32 Internet API, можно разделить на
четыре группы: первая содержит функции создания, инициализации и
управления сеансом обмена информацией с Internet, остальные три
- функции поддержки вышеуказанных протоколов.
Вне зависимости от того, с каким протоколом вы собираетесь
работать, необходимо выполнить ряд действий по инициализации
библиотеки и подключению к удаленному серверу. Для этого
используются функции первой группы: InternetOpen, которая
создает новый сеанс и возвращает указатель на него или нулевое
значение, если произошла ошибка, и InternetConnect, открывающая
существующий сеанс, указанный в качестве параметра при вызове
функции. По окончании работы для закрытия сеанса и освобождения
занимаемой памяти следует вызвать функцию InternetCloseHandle.
В случае успешного подключения и регистрации на удаленном
сервере можно использовать функции поддержки соответствующего
протокола. Причем синтаксис этих функций близок к синтаксису
стандартных функций Windows, а иногда и совпадает с ним, и
программисту уже необязательно знать детали протокола для
управления удаленным сервером. Так, например, функцией создания
нового каталога на FTP-сервере является FTPCreateDirectory,
имеющая в качестве аргументов только указатель на сеанс и имя
создаваемого каталога.
Ознакомиться с информацией о функциях, включенных в Win32
Internet API, можно на сервере Microsoft по адресу
http://www.microsoft.com/intdev/sdk/docs/wininet/. Работу с
Borland Delphi облегчает модуль на языке Object Pascal, который
пользователи также могут скопировать с сервера фирмы Borland по
адресу
http://netserv.borland.com/techsupport/delphi/devcorner/internet/internet.html.
Ориентированные и не ориентированные на соединение программы
Как следует из названия, эти типы сокетов отличаются друг от
друга прежде всего тем, что ориентированному на соединение
клиенту для осуществления обмена информацией необходимо быть
подключенным к удаленному серверу посредством вызова функции
Connect. При этом в дальнейшем не придется указывать адрес
назначения информации при ее передаче, поскольку соединение уже
установлено. Если же вы создаете программу-клиент, не
ориентированную на соединение, то достаточно сконфигурировать
сокет при помощи вызова функции Bind и использовать для передачи
данных функцию Sendto, а для приема - функцию Recvfrom, которые
требуют указания адреса удаленного компьютера. Работу
ориентированного на соединение клиента можно с некоторой
натяжкой сравнить с передачей какой-либо информации (голосовой,
факсов и т. п.) по телефону, а не ориентированного на соединение
- с отправкой писем по обычной (не электронной) почте.
Программы-серверы также могут быть ориентированными и не
ориентированными на соединение. Инициатива при обмене
информацией между удаленными компьютерами исходит от клиента,
поэтому сокет сервера, созданный им посредством вызова функции
Socket и настроенный с помощью функции Bind, независимо от типа
сервера находится в пассивном ожидании до поступления от клиента
запроса на установление соединения. Получив такой запрос,
ориентированный на соединение сервер открывает канал обмена
информацией с клиентом, которую он передает ему при помощи
функции Send, а принимает, используя функцию Recv. Не
ориентированный на соединение сервер при получении
информационного пакета от клиента в числе прочих данных выделяет
адрес клиента, отправившего данный пакет. Этот адрес
используется сервером при передаче информации клиенту при помощи
функции Sendto.
|