March 1996
![]()
The
Visual Programmer Fingers WinSock Functions from Visual Basic
Joshua Trupin
Joshua Trupin – разработчик программного обеспечения, специализирующийся на C/C++ и Visual Basic приложениях для Windows. Его можно найти 75120.0657@compuserve.com or geeknet@ix.netcom.com.
Открыть или копировать файлы проекта VISPRG.
В настоящее время каждый хочет выходить в Internet. Включая седых ветеранов вроде меня, которые вспоминают, когда был ARPANet. Только я говорю это не для того, чтобы выглядеть более осведомленным по теме. Я догадываюсь, что я только пытаюсь выглядеть более осведомленным. Так или иначе, этот прилив волны интереса много сделал для программирования. Во-первых, это создало жизнеспособный рынок для таких программ клиента как mail readers и Web browsers. Во-вторых, это воспитало программистов, подобных мне, которые избегали сетевых протоколов и программирования со всей жестокостью борющегося до конца участника состязания, реально только то что мы лучше постигали что такое TCP/IP. (В случае, если вы в прошлом году прятали голову в песок, TCP/IP означает Transmission Control Protocol/Internet Protocol.)
TCP/IP - это просто
набор протоколов. Часть
TCP описывает как два компьютера устанавливают
надежное соединение с любым другим и передают
порции данных. IP главным образом распоряжается
как получить сообщение, направленное через Internet.
Каждый соединенный компьютер имеет уникальный IP
адрес, который позволяет другим вычислять
маршрут к любому компьютеру в мире. Каждый
компьютер с IP адресом поддерживает нумерованные
порты, каждый выделен для индивидуального
сервиса. Как отдельный пример, вы используете TCP
чтобы отправить почту через Simple Mail Transfer Protocol (SMTP)
используя порт 21. Вы получаете почту по протоколу
Post Office Protocol (POP) на порт 110. Эти номера портов
должны быть выбраны по соглашению. Таким образом,
компьютеру нужно работать с обоими протоколами
TCP и IP совместно (набор протоколов) для связи с
внешним миром.
Так как Internet
построен на соединениях TCP/IP, программы клиента,
имеющие доступ в Internet, должны использовать TCP/IP
для соединения с удаленными компьютерами и
передачи данных в различных форматах. Удачно, что
использован стандарт API чтобы писать на сокеты
TCP/IP. Сокеты были первоначально разработаны для
Unix; Windows Sockets, или WinSock сокращенно, были позже
разработаны для программ под Windows® (Подробнее, см.
"Plug into Serious Network Programming with the Windows Sockets API" by J.
Allard, Keith Moore, and David Treadwell, MSJ, July 1993). Здесь, я буду
разрабатывать маленький OCX, который обеспечивает
избранные функции WinSock, потом писать программу
для 32-bit Visual Basic®, которая выдает команду ,
запрашивающую у удаленного компьютера
информацию об определенном пользвателе.
Во-первых, кусочек
заднего плана WinSock. Когда программа коммуникаций
WinSock послушна, это обычно означает, что после
набора номера и соединения с удаленной службой
провайдера, это обеспечивает подключение к
сокетам для ваших программ клиента. Каждая
коммуникационная программа, которая
осуществляет эти услуги TCP/IP, реализует
собственное выполнение WINSOCK.DLL. Эти DLLs, как
правило, несовместимы с другим уровнем сервера,
хотя они обеспечивают тот же внешний сервис
клиентам, желающим связаться с ними. Поэтому,
если вы переписываете другие коммуникационные
программы на WINSOCK.DLL, те программы не будут
работать корректно, но программа клиента, как
например, моя программа finger (обсуждена позже)
будет работать таким же образом с любым модулем
WINSOCK.DLL. Если это кажется непонятным Вам, вы
имели больше шансов уйти от проблем такого рода
чем многие другие.
Нужно знать другое -
программа может обеспечить 16-bit или 32-bit
реализацию WinSock. Если ваша коммуникационная
программа 16-bit, вы не можете выполнить с ней 32-bit
пппрограмму клиента WinSock. Однако, если вы имеете
32-bit коммуникационную программу или
номеронабиратель (как например the Windows 95 Dial-Up
Networking), вы можете использовать с ней 16-bit и 32-bit
программы клиента. Разрабатывая программы в этом
месяце, я использовал мою учетную запись на
Netcom(популярная служба провайдера). Netcom
обеспечивает только 16-bit набиратель номера,
NetCruiser, но установив Windows 95 dialer вместо него, я смог
легко установить соединение через более
надежное 32-bit соединение.
Одна из самых
больших забот в разразработке программы WinSock то,
что не все запросы данных выполняются
немедленно. Если вы ожидаете ответа или
уведомления от удаленного компьютера, это может
занять несколько секунд или даже минут прежде
чем сообщения проделают обратный путь через
Internet. По этой причине функции WinSock разделены на
две группы, синхронные (возвращающие данные
немедленно) или асинхронные (возможно, вам
пришлось бы ждать возврата сообщения).
Синхронными сообщениями считаются те, которым не
нужно идти через сеть для возврата данных, как
например, вызовы для преобразования порядка
следования байт от сети к клиенту. Асинхронными
вызовами считаются вызовы, подобные удаленным
соединениям, где вы сразу не будете знать
отработал ли он.
Если ваша программа
отправляет сообщение компьютеру через полмира и
ожидает ответа, может показаться, что она
зависла. Вам следует писать собственный
обработчик, чтобы обрабатывать другие сообщения
Windows во время ожидания, или вы будете
рисковать заморозить другие программы пока ваше
маленькое себялюбивое приложение ждет
поступления данных (во всяком случае в 16-bit Windows),
проблема зачастую известна как синдром WinCIM.
Когда вы должны
делать вызов WinSock, который требует асинхронных
операций, вам нужно обработать этот запрос. В
прошлом месяце, в моем разделе Visual Programmer, я
спорил, что многие функции обратного вызова в
Windows 95 разработаны так, что предполагают наличие
описателя окна и идентификатора сообщения.
Асинхронные вызовы WinSock используют этот
механизм. Если вы предполагаете, что поступят
некоторые данные от компьютера из внешнего мира,
вы можете вызвать функцию WSAAsyncSelect, и передать ей
описатель окна, идентификатор сообщения, и флаг,
показывающий, что вы хотите ожидать данные:
WSAAsyncSelect(socketID, hWnd, WM_USERDEFINED, FD_READ)
Когда данные
действительно поступают откуда-то из сети и
попадают в ваш стек TCP/IP, ваша процедура обработки
сообщения окна получает управление. Между тем,
ваш интерфейс не замораживается и вы можете
перейти к другому процессу.
Теперь допустим,
что вы хотите делать все это в Visual Basic. Так как вы
не можете использовать Visual Basic чтобы
перехватывать сообщения пользователя в пределах
кода формы Visual Basic, вам нужно оказать некоторую
помощь. Вам нужен WinSock OLE control, который может
обрабатывать те асинхронные вызовы и вызвать
событие в Visual Basic, когда что-то случается.
Чтобы
продемонстрировать как легко можно начать
программирование с WinSock, я разработал Flipper: Это
программа MSJ Finger. Я предпочитаю писать
программу-указатель, потому что это одна из
постейших вещей, которую вы можете делать в Internet.
Это не требует от вас поддерживать соединение с
удаленным компьютером через сеанс FTP (file transfer
protocol). Все что вы должны сделать это найти
уникальный цифровой IP адрес удаленного узла,
отправить строку, содержащую имя пользователя,
узлу с указанным IP адресом, и ждать возврата
данных. Когда ваш компьютер получит обратно
данные от того IP адреса, вашей программе
сообщается, данные получены, и вы берете их и
отображаете(см Рис. 1).

Рисунок 1
Прикосновение к удаленному пользователю через
WinSock
Возможно, я только
должен опредеить что такое программа finger. Это
простой запрос, посланный удаленному узлу, чтобы
извлечь информацию о пользователе или
пользователях в системе. Для любых служб Internet,
подобных обозрению фалов, передаче (FTP) и доступу
к удаленному терминалу (Telnet) нужно полное
соединение, с процедурами передачи пароля и
устойчивой связью. Finger, однако, не нуждается ни в
каких подобных выдумках. Когда вы отправляете
заканчивающееся символами CR/LF имя пользователя
как сообщение указанному услу сети, он
отправляет обратно блок текстовой информации о
том пользователе. Эти данные определены
отдельным компьютером; могут содержать
информацию о соединении, настоящее имя, объем
почты, которую ожидает пользователь, или даже
отображать определенную пользователем
информацию (общеизвестную как .plan файл). После
того как информация возвращена вы ничего больше
не обязаны. (Только пишите CANCEL и вы ничего не
обязаны!)
Так давайте
перейдем к действиям, нужным для реализации
программы finger. Во-первых, вы должны соединиться
со стэком TCP/IP и получить socket handle для
использования. Потом вы должны извлечь адрес
удаленного узла, соединиться с ним, отправить ему
имя пользователя, интересующего вас, подождать
возврата информации, и удалить соединение.
Прежде всего я
создал MSJSock control средствами Microsoft Developer Studio (AKA Visual
C++® 4.0). Чтобы получить команды finger, работающие
корректно, control'у нужно пять методов: CreateSocket,
gethostbyname, getservbyname, AsyncSelect, и Connect. Технически,
некоторые из этих вызовов WinSock могут быть сделаны
из Visual Basic, но запуск в MSJSock OCX делает некоторые
операции легче. Например, если вы не знаете какой
стандартный порт использовать для особого
сервиса, вы можете метод getservbyname - интерфейс с
функцией API. Если вы делаете вызов
getservbyname("finger", "tcp")
вам возвращается
информация о finger согласно протоколу TCP, но она
возвращена как структура в неприглядном виде как
например указатели к указателям. С такой
информацией легче работать на C/C++, потому что Visual
Basic плохо поддерживает указатели. В Windows 3.1, вы
имели бы потенциальную возможность вызвать hmemcpy,
API функцию доступа к памяти из библиотеки KERNEL.DLL. К
сожалению, эта функция больше не поддерживается
KERNEL32. DLL, и вы должны экспортировать ваш
собственный равноценный метод или создать вызов,
который возвращает отдельный элемент структуры.
Я выбираю второй путь, который менее гибкий, но
ведет к более простому коду Visual Basic. Просто, когда
вам нужно вызвать асинхронную функцию WinSock, OLE
control обеспечит вам перехват сообщения, и будет
возбуждаться событие в проекте Visual Basic, когда
поступают данные из сети. Ясно, что OCX и проект Visual
Basic нужны друг другу, чтобы работать корректно,
хотя control не будет связывать один отдельно взятый
фрагмент кода Visual Basic.
Клиенту нужно
представить только две части информации ID
пользователя и имя узла. Поэтому, VB - программа
Flipper может быть выполнена с явно обнаженным
исполнением скелета. Я добавил два элемента
редактирования (один для каждого фрагмента
информации), text box для отображения результатов,
кнопку. которая запускает запрос, и MSJSock control.
Когда в элементы редактирования занесены имя
пользователя и имя удаленного узла, кнопка
является доступной. Когда происходит щелчок,
программа переходит к действию. Вызывается метод
CreateSocket MSJSock control'а, который в свою очередь
вызывает функцию WinSock. (Код MSJSock можно найти в Figure 2;
проект Flipper показан в Figure 3.)
Узел немедленно возвращает свой ID, так вы
имеете handle, чтобы передавать данные,
представленные задачей.
Когда вы пишете
программу с WinSock, первое, что нужно сделать вашему
приложению - инициировать обслуживание узла для
примера с вызовом WSAStartup. WSAStartup заносит в
структуру какую-либо показательную информацию,
вроде текущей версии WinSock и строку описания,
например "Выполняется под Windows 95." Таким
образом, я, используя мой MSJSock OCX, делаю некоторые
из вызовов, передающих структуры, более легкими,
я должен сделать вызов WSAStartup в функции InitInstance из
MSJSock.
Затем, программа на
Visual Basic вызывает метод getservbyname control'а , чтобы
получить корректный ID порта для запроса. Как я
упомянул ранее, узел может поддерживать больше
чем одну функцию, один сервер может обеспечить
оба сервиса - finger и FTP. Это делается через
выделенный номер порта. Понятно, что когда
пользователь отправляет сообщение порту finger
(обычно N 79), они посылают команду. Однако, не
исключено, что сервер имеет нестандартную
реализацию, или вы используете
специализированный сервис, или вы только хотите
убедиться, что ваш локальный сервер поддерживает
особый интерфейс, WinSock обеспечивает getservbyname, так
что вы можете быть уверены, что там не
выполняется бессмысленная работа.
Теперь вы должны
отыскать IP адрес для узла, указанного вами. Чтобы
отслеживать все узлы одним способом, каждому
узлу, когда он создан, назначен адрес в виде
длинного целого числа. Провайдеры (например MSN)
знают как преобразовать строчный адрес, например
"ftp.microsoft.com" в сответствующий длинный целый.
Иногда это число разбивается на 4 байта, так что
ftp.microsoft.com достижим по 32008646, но общеизвестно как
198.105.232.1 (198 + 105 x 256 + 232 x 2562 + 1 x 2563). Вы
будете довольны, узнав, кстати, что когда текущее
основание из длинных целых через несколько лет
исчерпается, IP адреса станут длиннее, общая
переделка программного обеспечения даже хуже
чем переход к 21-ому столетию, который страшнее
атомной для двузначного года, так популярного в
основных программах.
Извлечение хоста по
имени является асинхронной функцией. Когда вы
вызываете метод control'а gethostbyname, он отсылает запрос
и немедленно возвращает управление. MSJSock имеет
отдельную функцию обработки сообщения, которая
будет вызвана WinSock, когда информация будет
готова. эта функция (OnGetHostCB) вызывает событие GotHost
в Visual Basic. Программа на Visual Basic может свободно
делать что угодно пока событие MSJSock_GotHost вызвано.
Когда событие MSJSock_GotHost вызвано, OLE control передает
Visual Basic длинное целое, которое в действительности
является указателем на структуру, описывающую
адрес хоста. Вам не нужно снимать косвенность;
вам нужно только передать это обратно, когда вы
установите соединение с узлом хоста.
Чтобы получить
реальное соединение, вам нужно три
информационных фрейма: socket handle, полученный ранее,
the port ID порта для указания, и структура,
содержащая IP адрес узла. Соединение с удаленным
хостом может быть инициировано путем вызова
метода Connect из MSJSock control. MSJSock.Connect делает две вещи.
Первое, вызывает WSAAsyncSelect. WinSock использует эту
функцию. чтобы определить куда отправить
асинхронное уведомление для частного сокета.
Вызывающая программа определяет за каким видом
даных наблюдать. Здесь, вы передаете флаг FD_CONNECT
функции WSAAsyncSelect; это сообщает WinSock чтобы control знал
когда дествие соединения закончено. Тогда control
может проверить код ошибки - если 0, он возбуждает
событие MSJSock1_Connect в Visual Basic. В противном случае,
вызывается MSJSock1_SockError чтобы индицировать, что
произошла ошибка. Я должен поставить блок Select Case
в WINSOCK.BAS, который будет согласовывать известные
WinSock коды ошибок с их наименованиями, так
программа может высветить на экране MsgBox.
Вы теперь прошли
больше чем полпути: определение адресов и
соединений - самая сложная часть
программирования с WinSock. Когда MSJSock_Connect сработало
внутри Flipper, это означает, что вы корректно
соединились с удаленным портом узла. Все что
осталось finger это отправить имя пользователя,
законченное парой CR-LF. В control'е, я показал метод
AsyncSelect, который представляет оболочку функции
WSAAsyncSelect. На сей раз Вы хотите перехватить
сообщение в любое момент, когда WinSock сообщает Вам,
что имеются данные от удаленного узла, который
можно читать, поэтому передайте FD_READ. Вы можете
теперь вызвать функцию WinSock send непосредственно
из Visual Basic, указав ID узла и строку. Начиная с более
раннего вызова Connect, связавшего указанный
удаленный узел и порт с полученным сокетом, вы не
должны сообщать WinSock по какому пути данные пойдут
повторно.
Вызов send в
действительности не асинхронный, потому что он
не гарантирует, что данные вернутся. Однако,
удаленный хост обычно что-то делает в ответ, он
отправляет обратно информацию пользователя и
статус. Когда MSJSock control фиксирует уведомление FD_READ
от WinSock, он генерирует событие RecvData в Flipper. Это
только сообщает программе на Visual Basic, что данные
доступны для извлечения, таким образом, работа
программы сводится к вызову функции WinSock recv в
ответ. recv принимает три параметра: ID узла, пустую
строку, куда будут положены данные, и
максимальную длину этой строки. Вместо
объявления строки фиксированной длины в Visual Basic,
я объявлял строку переменной длины, потом
инициализировал ее пробелами:
strg$ = Space(1024)
После того как эта
строка передана recv, она может быть усечена (с
использованием функции Trim), потом отображена в text
box, который я добавил в программу как раз для этой
цели . Если все идет хорошо, программа Flipper
завершается (см. Figure 4). Если же что-то идет
неправильно, она отображает код ошибки.

Figure 4 Flipper
OK, так называемый
finger - один из самых надежных классов по
сравнению с другими протоколами Internet. Он не
обслуживает соединение и не заставит Вас изучить
группу команд, чтобы работать должным образом. К
несчастью, описание каждой и всякой доступной
функции потребовало бы написать книгу или две, а
то и целую книжную полку, если вы посещаете свой
местный книжный магазин. Однако, у вас нет причин
отлучаться с работы. Есть большая
потребность в 32-разрядных программах считывания
новостей с коммерческим качеством, Telnet
программы, и что - нибудь подобное, развитие чего
могло бы заинтересовать вас.
Различные
служебные протоколы описаны в документах ,
названных RFC (Request For Comments) и STD (стандарты). Первое,
что следовало бы сделать, - определить какой тип
программы пишется, потом овладеть документацией,
чтобы знать какие команды поддерживает сервер
(см. Figure
5). Там около 2000 Internet, TCP/IP, и сокетов RFC,
доступных в настоящее время, проглядывая все в
обратьном порядке до 1969 года. Есть нестолько
источников RFC. Если у вас есть программное
обеспечение Internet, вы можете зайти анонимно по ftp
на ds.internic.net, потом извлечь файлы из директрия /rfc.
Исключительная книга, Developing for the Internet with Winsock
by Dave Roberts (Coriolis Group Books, 1995), поставляется на CD-ROM с
каждой частью Internet и документацию вы найдете в
любое время. Figure 5 показывает наиболее общие
службы Internet и соответствующие RFC.
Чтобы помочь вам в отладке программ, я разработал маленькую программу под названием Simple Sockets. Это интерактивный диалог, который позволяет вам посылать команды на сервер и видеть что возвращается от него. Полная транзакция отображена в окне редактирования RichText Windows 95, помеченном цветом, так вы легко можете видеть что она из себя представляет.
Как пример, я
загрузил RFC 0977 (протокол новостей NNTP) и теперь я
хочу только видеть как это работает. Как вы
знаете или не знаете, Usenet - название для сети
серверов, которые торгуют статьями,
отправленными по почте пользователям, по любой
теме, какую только можно предположить (всего
около 16,000 наименований). Используя программу
чтения новостей, вы можете просматривать статьи
по заголовкам или по номерам, отправлять по почте
отклики, или сохранять или печатать особенно
полезные выдержки. Это распределенная
система без отдельного места расположения, так
что вы убедитесь, что видели много бесплодных
усилий бюрократов издать законы,
регламентирующие содержание, за последние
несколько лет.
Мне нужно две вещи
для соединения: имя моего сервера новостей
(nntp.ix.netcom.com) и порт NNTP port (стандартный 119; это дано в
RFC). Я ввожу два значения и щелкаю "connect", и
Windows 95 или другой выполняет auto-login с адаптером
набора номера для меня или соединяет меня через
WinSock. Действия, не возвращающие текст подобно
этому показаны как информационные сообщения
синим цветом. Когда программа индицирует
"Connected to host," я могу начать отправку моих
команд, которые выделены зеленым цветом. Всякий
раз, когда моя программа получает что-то от хоста,
оно добавляется к окну RichText красным цветом, и
должна произойти ошибка WinSock (плохое имя сервера,
например), сообщение будет показапно синим
цветом (см. Figure 6).

Figure 6 MSJ Simple Sockets
На своем сервере, я
должен регистрироваться командой AUTHINFO,
обеспечивающей мое имя пользователя и пароль. Я
ввожу это в поле редактирования, потом щелкаю
кнопку Send или нажимаю "enter". Через пару
секунд, я получаю информационное сообщение от nntp.ix.netcom.com:
"501 user Name|pass Password." Отыскав код сообщения 501 в
RFC, я сообщил, что в моей команде была
синтаксическая ошибка. Я не расстраиваюсь,
потому что это, кажется, так или иначе работает.
Не вдаваясь во
многие детали, NNTP сервер обеспечит список всех
доступных групп новостей по команде LIST.
Скуджные возможности, но несмотря на это
возвращается все 16,000 групп, плюс или минус. Как
только вы получаете этот однажды выгруженный
список, вы можете использовать команду NEWGROUPS,
чтобы увидеть все дополнения от определенной
даты.
Figure 6 показывает
некоторые другие команды NNTP в действии. Я решил
проверить действие на alt.fan.surak. (И мне не
нужно редакционных комментариев.) Команда GROUP
возвращает информационный код (211, который
означает, что это ответ команды GROUP, как
документировано в RFC). Он также сообщает вам номер
доступной статьи, первой и последней доступной
статьи, и имя группы. Вместо того, чтобы делать
это для 16,000 групп каждый раз пользователь
запускает программу, программы считывания
новостей обычно имеют локальную опцию
"subscribe", которая реально только создает
внутренний список групп, содержание которых
проверяется автоматически.
После выборки
группы командой GROUP, Неявно принимается, что вы
работаете с ней, используя дальнейшие команды.
Для примера, я выдал команду ARTICLE, чтобы получить
отдельную нумерованную статью(наряду с парой
плохих команд только, чтобы показать, что они
могли быть выданы). Статья 2615 возвращена моей
программе, заголовки, текст и все остальное.
Только теперь я немного больше постигаю с
веяниями мира Surak. Я знаю, что это слишком хорошо,
чтобы быть правдой. Так или иначе, исходный код MSJ
Simple Sockets показан в Figure 7.
Естественно, много работы по слежению, чтобы сделать развитую программу чтения новостей. Однако, c MSJ Simple Sockets, вы можете видеть что в действительности происходит за кулисами NNTP, или любого другого протьокола, который вы хотели бы испытать
Программирование
Windows Socket в настоящее время только путь к каждому
протоколу и функции, обеспеченной в Internet. Однако,
вплоть до настоящего времени это было вне
пределов досягаемости программистов на Visual Basic. Я
обеспечил две компоненты, чтобы это изменить -
MSJSock OLE control и приложение Visual Basic MSJ Simple Sockets. С
удивительно маленьким кодом, вы можете писать
реальные программы, работающие в Internet на Visual Basic. Если
вы занимаете какое-то место на рынке, все, что я
прошу - чтобы вы послали мне пару долей ваших
акций компании Интернета.
From the March
1996 issue of Microsoft Systems Journal.
Send feedback on this article. Find support options.
© 2000 Microsoft Corporation. All rights
reserved. Terms of use.