English version
    Главная Новинки Новости Рейтинг продаж Файлы/Download Клуб Профессионал Партнерская программа
Ваш кабинет
Если вы уже зарегистрированы, введите ваши данные:
Логин  
Пароль

Библиотека: К. Джамса, К. Коуп. Программирование для Internet в среде Windows

Глава     11


Асинхронные сокеты Windows

В девятой главе вы познакомились с системой имен доменов Интернет и узнали, как по компьютерному имени узнавать его адрес и наоборот. В главе 10 вы узнали, как передавать информацию удаленному компьютеру при помощи сокетов. В обеих главах при этом использовались функции интерфейса Winsock, работающие в среде Windows. Но ни в одной из учебных программ мы не употребили специальных расширенных функций Windows. А ведь они являются мощным инструментом программирования в этой среде и лучше всего удовлетворяют модели программирования в Windows.

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

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

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

  • Аргументы (параметры), указываемые при вызове типичной асинхронной функции.
  • Как создать асинхронную функцию для поиска в DNS.
  • Каким образом сообщения асинхронных функций идентифицируются обработчиком задач.
  • Как из сообщения Windows извлекается сообщение об ошибке.

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

11.1 Шаблон программы Sockman

Шаблон программы Sockman, с которой вы будете работать на протяжении всей второй части книги, объясняется в приложении Б. Если вы еще не ознакомились с приложением Б, следует сделать это прямо сейчас. Набор команд и операторов, из которых составлен шаблон, не выполняет никаких сетевых действий. Вместо этого он создает Windows-интерфейс для вас и ваших сетевых приложений. Создание любой программы Windows требует приложения усилий, не связанных напрямую с решаемой задачей. Такая простая, казалось бы, операция, как отображение и ввод данных из диалогового окна, требует от программиста достаточно серьезных усилий. Это лишь одна из причин, почему асинхронные Windows-функции не обсуждались в двух предшествующих главах.

В этой главе мы начнем модифицировать шаблон Sockman, описанный в приложении Б. А именно, мы встроим в него функции по преобразованию имен и адресов в формате «десятичное с точкой» сетевых компьютеров в 32-разрядные двоичные адреса. Внося изменения в программу, вы изучите основные шаги, необходимые для использования асинхронных функций. Мы не будем обсуждать те функции, которые описаны в приложении. Вместо этого мы рассмотрим только новые или модифицированные варианты функций шаблона Sockman. Кним относятся функции для преобразования сетевых имен и адресов в 32-разрядный IP-адрес.

Классы и образцы Windows
Операционная система Windows ориентирована на объект под названием «окно». В большинстве случаев «окно»— это просто кадр или поле, в котором программа может отображать данные. Windows посылает сообщения объекту «окно», если в системе происходят определенные события или если появляется сообщение от другого работающего приложения.
Каждое окно, открываемое программой, имеет собственный тип класса window. Класс window позволяет задавать параметры, такие как размер, цвет или расположение меню пользователя. Эти характеристики являются общими для всех окон, принадлежащих данному классу. Для каждого типа класса window существует процедура (функция), обрабатывающая сообщения для окон этого типа.
Класс window и процедура-обработчик сообщений соотносятся между собой просто. Каждый раз, когда Windows генерирует сообщение для определенного объекта, оно посылается процедуре класса window, связанной с этим объектом. Программа может открыть несколько окон одного и того же класса. Каждое окно, принадлежащее данному классу, называется его образцом (instance). Другими словами, в системе одновременно могут существовать несколько образцов одного и того же класса.
Если в системе несколько образцов, в процедуре-обработчике сообщений должен быть механизм распознавания, какому из образцов адресовано то или иное сообщение Windows. Для этого каждому образцу данного класса присваивается уникальный номер-дескриптор. Когда посылается сообщение, в нем, в качестве одной из составляющих, всегда передается дескриптор того образца, кому оно адресовано. Для того чтобы успешно программировать в оболочке Windows, необходимо четко сознавать взаимоотношения между классом window, процедурами и дескрипторами окон.

11.2 В Sockman добавляется поиск в DNS

На приложенной к книге дискете находятся исходные тексты программы Sockman, а также ее готовые исполняемые файлы. Программа под названием Sockman2 состоит из шаблона Sockman, в который добавлены функции для работы с DNS.

Для ввода параметров используется диалоговое окно под названием IDD_TEXT. Оно изображено на рис. 11.1. Окно IDD_TEXT является просто устроенным окном общего назначения и позволяет вводить информацию в пределах одной строки.


Рис.11.1 Диалоговое окно IDD_TEXT из меню для работы с DNS программы-шаблона Sockman

Программа Sockman2 дает возможность производить блокирующий или не блокирующий поиск компьютерного адреса в DNS по имени или по адресу в формате «десятичное с точкой». Для этого в программе предусмотрены две функции: синхронная LookupHostBlocking для блокирующего сокета и асинхронная LookupHostAsync.

Как мы писали в приложении Б, большинство задач по обработке сообщений Windows берут на себя три функции (WndProc, WinMain и DoMenuCommand). Главное окно Sockman передает сообщения WinMain, а она, в свою очередь— функции WndProc (главная функция-обработчик сообщений). Далее, все сообщения WM_COMMAND передаются в DoMenuCommand. Функция DoMenuCommand обслуживает все сообщения Windows, не касающиеся сетевой работы, например опции меню File или Help. По умолчанию DoMenuCommand передает все остальные сообщения функции DoWinsockProgram. На рис. 11.2 изображен маршрут движения сообщений в приложении Sockman.


Рис.11.2 Основной маршрут движения сообщений в приложении Sockman

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

11.3 Как изменять функцию DoWinsockProgram?

В тот момент, когда пользователь выбирает пункт Async Lookup или Blocking Lookup из меню Utilities, подменю Lookup, окну Sockman посылается сообщение WM_COMMAND. Функция WndProc пересылает его DoMenuCommand, которая, в свою очередь, вызывает функцию DoWinsockProgram.

Примечание:Функция DoWinsockProgram всегда присутствует в исходном тексте SOCKMAN.CPP. Исходный текст второй версии Sockman соответственно находится в файле SOCKMAN2.CPP. В третьей версии Sockman функция DoWinsockProgram находится в файле SOCKMAN3.CPP и т.д.

В следующем фрагменте программы показано, как функция DoWinsockProgram обрабатывает оба пункта подменю Lookup (Async Lookup и Blocking Lookup):

long DoWinsockProgram(HWND hwnd, UINT wParam, LONG lParam) 

{ 

switch (wParam) 

{ 

case IDM_LOOKUP_ASYNC: 

case IDM_LOOKUP_BLOCKING: 

if (LookupHostDialog()) 

{ 

if (wParam == IDM_LOOKUP_ASYNC) 

hAsyncLookupTask = LookupHostAsync(hwnd,szLookupText, szLookupBuffer, 

(LPDWORD)&dwLookupAddr); 

else 

LookupHostBlocking(hwnd,szLookupText,szLookupBuffer, 

TASK_BLOCK_LOOKUP); 

} 

return(TRUE);

Если пользователь выбрал пункт меню Async Lookup, переменная wParam получит значение IDM_LOOKUP_ASYNC. Если выбран пункт Blocking Lookup, параметр wParam получит значение IDM_LOOKUP_BLOCKING.

В любом случае функция DoWinsockProgram вызовет функцию LookupHostDialog. Она, в свою очередь, выведет на экран диалоговое окно IDD_TEXT, изображенное на рис. 11.1. Получив данные от пользователя, программа затем выполнит требуемую операцию по поиску в DNS.

11.4 Подробнее о диалоговом окне

Диалоговое окно IDD_TEXT конструируется следующими операторами (их можно найти в файле SOCKMAN2.RC):

IDD_TEXT DIALOG 

DISCARDABLE 0, 0, 161, 65

STYLE DS_MODALFRAME | WS_POPUP 

| WS_VISIBLE 

| WS_CAPTION 

| WS_SYSMENUCAPTION "Enter Value"

FONT 8, "MS Sans Serif"

BEGIN 

EDITTEXT IDC_TEXTBOX,5,15,150,15,ES_AUTOHSCROLL 

DEFPUSHBUTTON "OK",IDOK,5,40,50,15 

PUSHBUTTON "Cancel",IDCANCEL,60,40,50,15

END

В диалоговом окне использован стиль WS_SYSMENU (в котором добавляется меню Control), кнопки OK и Cancel. Пользователь может выйти из окна, выбрав пункт Close меню Control или щелкнув мышью на клавишах OK или Cancel. Выход из окна будет успешным (IDD_TEXT вернет константу IDGOK), если пользователь нажмет на кнопку OK (по умолчанию). Если пользователь нажмет на кнопку Cancel, результат будет равен IDCANCEL.

11.5 Подробнее о диалоговой функции

Как уже говорилось, когда функция DoWinsockProgram вызывает LookupHostDialog, она, в свою очередь, генерирует и отображает диалоговое окно IDD_TEXT. Исходный текст функции LookupHostDialog находится в файле LOOKUP2.CPP. Она реализована при помощи следующих операторов:

LPSTR LookupHostDialog(VOID) 

{ 

// Диалоговая процедура использует глобальный статический 

// буфер szLookupText для хранения введенной  

// пользователем в текстовом окне информации. Вызывающие  

// процедуру функции должны немедленно скопировать 

// эту информацию в собственные внутренние буферы, так 

// как глобальный буфер стирается программой поиска в DNS.DLGPROC 

lpfnDialogProc;

 BOOL bOkay;

 // Создать диалоговое окно 

lpfnDialogProc = MakeProcInstance((DLGPROC)LookupHostDialogProc, 

hInstanceSockman); 

bOkay = DialogBox(hInstanceSockman,"IDD_TEXT", hwndSockman, 

lpfnDialogProc); 

FreeProcInstance(lpfnDialogProc);

return(bOkay ? 

szLookupText : (LPSTR) NULL); 

}

Для генерации и отображения диалогового окна в функции LookupHostDialog используется стандартная техника. Вначале вызывается функция Windows MakeProcInstance, аргументом которой указывается функция обратного вызова (call-back) LookupHostDialogProc. Результатом MakeProcInstance является пролог (стартовый код) указанной в качестве аргумента функции, в нашем случае— функции LookupHostDialogProc. Когда в дальнейшем Windows посылает сообщение диалоговому окну, на самом деле сообщение посылается функции, указанной в прологе.

Что такое функции обратного вызова?
Каждый класс window имеет соответствующую функцию-обработчик сообщений для объектов данного класса. Такая функция, описанная в структуре класса window, называется функцией обратного вызова (call-back). Она называется так потому, что Windows вызывает ее при поступлении сообщения для данного класса объектов. Короче говоря, Windows передает сообщения функции обратного вызова, а та, в свою очередь, обрабатывает эти сообщения.
Для каждого класса window в вашей программе должна существовать функция обратного вызова. Как правило, это просто, если и описание класса и функция обратного вызова определены в одной и той же программе. Трудности возникают только тогда, когда описание класса находится за пределами программы, как, например, описание класса диалогового окна, которое используется в функции Windows DialogBox. Предположим, вы хотите, чтобы сообщения класса диалогового окна передавались в вашу программу. Для этого Windows должна вызывать вашу собственную функцию. Поскольку ваша функция-обработчик сообщений находится в адресном пространстве вашей программы, а описание класса диалогового окна— вне его, Windows должна выполнить определенные действия до того, как передать сообщение. Инструкции-операторы, выполняющие эти действия, находятся в области под названием «пролог» или «стартовый код» функции.
Перед тем как функция обратного вызова получит сообщение, Windows выполнит операторы из пролога функции. С другой стороны, если функция обратного вызова вызывается из самой программы, эти операторы не выполняются. После того как функция обратного вызова закончила работу, Windows выполняет последовательность инструкций из эпилога функции. Опять-таки, если функция вызвана из программы, эти инструкции не выполняются.
Чтобы правильно определить пролог и эпилог при компиляции, в исходный текст программы необходимо поместить специальный макрос CALLBACK. Его значение определено в файле-заголовке windows.h. Дополнительно компилятору задаются специальные флаги. Как правило, если вы определили и зарегистрировали собственный класс window, вам не нужно заботиться о прологе и эпилоге функции обратного вызова. Пролог и эпилог нужны, только когда программа и описание класса находятся в разных областях памяти компьютера.

Адрес пролога функции обратного вызова (полученный в результатевызова MakeProcInstance) заносится LookupHostDialog в локальную переменную lpfnDialogProc. Далее вызывается функция Windows DialogBox. В параметрах вызова указывается дескриптор образца задачи Sockman. (Дескриптор образца задачи описывает конкретный экземпляр работающего приложения. Вы знаете, что можно запустить несколько копий одной и той же программы одновременно.)Функция LookupDialogBox также задает название диалогового окна IDD_TEXT, дескриптор окна Sockman (для того, чтобы Windows знала, кому адресованы сообщения Sockman) и переменную lpfnDialogProc (в которой хранится адрес пролога функции LookupHostDialogProc). Другими словами, вызов DialogBox создает новое диалоговое окно IDD_TEXT и указывает функцию обратного вызова LookupHostDialogProc, которой будут доставляться все сообщения от данного окна.

Подробнее о прологе и эпилоге функции
Вы знаете, что, если функция обратного вызова и описание класса window находятся в разных областях памяти, компилятор должен генерировать специальный код пролога и эпилога. Код пролога выполняется до операторов функции, а код эпилога— после. Дело в том, что, если описание класса window и программа находятся в разных областях памяти, это значит, что они пользуются различными сегментами кода и данных. Макрос CALLBACK, включенный в программу, указывает компилятору, что в этом месте должен быть вызов функции типа FAR (дальний). То есть компилятор делает так, что разница между сегментами кода исчезает. Чтобы устранить несоответствие между сегментами данных, перед каждым вызовом функции процессор должен установить значение регистра сегмента данных так, чтобы он указывал на область данных функции обратного вызова. Эта задача и решается операторами пролога и эпилога.
До начала выполнения функции обратного вызова инструкции пролога устанавливают регистр сегмента данных (DS) на область данных функции. При этом регистру DS присваивается содержимое регистра AX. Операция (специальный код), приводящая к тому, что в регистреAX появляется значение сегмента данных функции обратного вызова, называется «thunk».
До того как функция отработает, операторы эпилога восстановят значение регистра DS из стека. Опять-таки, поскольку мы использовали макрос CALLBACK (и нужные ключи при компиляции), компилятор знал, что функция снабжается прологом и эпилогом, поэтому позаботился о том, чтобы значение DS попало в стек.

Когда пользователь закрывает или выходит из диалогового окна IDD_TEXT, функция DialogBox возвращает управление в вызывавшую функцию LookupHostDialog. Она в свою очередь освобождает ресурсы, занятые обработчиком LookupHostDialogProc. Если пользователь нажал кнопку OK, LookupHostDialog вернет значение-указатель на глобальную переменную szLookupText, содержащую информацию, введенную пользователем. В противном случае, LookupHostDialog возвращает NULL.

11.6 Что такое диалоговая процедура?

Функция обратного вызова LookupHostDialogProc обрабатывает все сообщения Windows, поступающие от диалогового окна IDD_TEXT. Вот операторы, из которых она состоит:

BOOL _export CALLBACK LookupHostDialogProc(HWND hwndDlg, 

UINT iMessage, WPARAM wParam, LPARAM lParam) 

{ 

switch (iMessage) 

{ 

case WM_INITDIALOG: 

// Инициализация

// диалогового окна 

SetDlgItemText(hwndDlg, IDC_TEXTBOX,(LPSTR)szLookupText ); 

SetWindowText(hwndDlg,"Enter HOST Name or IP Address");

CenterWindow(hwndDlg); 

return(TRUE);

 case WM_CLOSE: 

// Обработать также, как и Cancel 

PostMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0L); 

return(TRUE);

 case WM_COMMAND: 

switch (wParam) 

{ 

case IDOK: 

// Обработка нажатия кнопки OK 

GetDlgItemText(hwndDlg, IDC_TEXTBOX,  (LPSTR)szLookupText, 

MAX_HOST_NAME); EndDialog(hwndDlg, TRUE); 

return(TRUE);

 case IDCANCEL: 

// Обработка нажатия кнопки Cancel 

EndDialog(hwndDlg, FALSE); 

return(TRUE);

 default: return(TRUE); 

} 



} 

return(FALSE); 

}

Как видим, функция LookupHostDialogProc обрабатывает три сообщения Windows: WM_INITDIALOG, WM_CLOSE и WM_COMMAND. Сообщение WM_INITDIALOG посылается перед открытием диалогового окна. Оно используется функцией LookupHostDialogProc для инициализации диалогового окна.

О стиле написания программ
Взглянув на исходный текст функции LookupHostDialog, опытный программист сразу обратит внимание на избыточное количество операторов перехода goto и операторов возврата return. И это не будет большой неожиданностью, поскольку слишком большое количество таких операторов говорит о том, что программа была спроектирована неудачно. Мы, создатели этой программы, полностью поддерживаем и одобряем следующую концепцию:
Избегайте использования операторов goto всеми возможными способами. Функция должна иметь только одну точку выхода— оператор return. Следуйте этим правилам во всех случаях— за исключением тех, когда нарушение правил приведет к большим удобствам при сопровождении и лучшему пониманию смысла программы.»
Главная цель программ-примеров этой книги— дать вам понять, каким образом программа следует тому или иному сетевому протоколу Интернет. Чтобы не отвлекаться и сфокусировать ваше внимание именно на проблемах разработки приложений Интернет, мы пренебрегали сложившейся в программировании практикой. Это позволило нам яснее выражать некоторую важную информацию. Ситуация с большим количеством операторов возврата— один из таких случаев.
Некоторые функции анализируются в книге по частям. То есть изучение фрагментов занимает значительную часть вашего времени, и множество точек возврата помогает нам в этом процессе. Разумеется, точки возврата можно было бы устранить при помощи вложенных конструкций из операторов if и получить хорошо структурированный код. Но наша главная цель— объяснение концепций программирования приложений Интернет— так и не была бы достигнута.

11.7 Инициализация диалогового окна

Когда функция LookupHostDialogProc получает сообщение WM_INITDIALOG, она инициализирует содержимое текстового окна IDC_TEXTBOX данными, предварительно введенными пользователем программы Sockman. Эти текстовыеданные находятся в глобальной переменной szLookupText. Содержимое szLookupText выводится в диалоговое окно IDD_TEXT. Чтобы присвоить IDG_TEXTBOX значение по умолчанию, функция LookupHostDialogProc вызывает функцию Windows SetDlgItemText, которая и присваивает значение переменной szLookupText содержимому текстового окна IDC_TEXTBOX. Кроме того, LookupHostDialogProc устанавливает значение заголовка окна: «Enter HOST Name or IP Address». Наконец вызывается функция CenterWindow (ее текст находится в файле COMMON2.CPP). Она центрирует диалоговое окно по отношению к верхней части главного окна Sockman. После того как инициализация успешно закончена, функция CenterWindow возвращает Windows значение true.

11.8 Команды диалогового окна

Как мы уже говорили, пользователь может закрыть диалоговое окно при помощи пункта меню Control под названием Exit, либо щелкнув мышью на кнопках OK или Cancel. При этом Windows генерирует сообщение WM_CLOSE, если окно закрыто через меню, либо WM_COMMAND, если была нажата одна из кнопок. Если нажата кнопка Cancel, сообщение WM_COMMAND содержит параметр IDCANCEL. Когда функция LookupHostDialogProc получит такое сообщение, она вызовет функцию EndDialog с параметром false. Вызов функции EndDialog приводит к уничтожению окна. Уничтожение, однако, произойдет не раньше, чем функция DialogBox закончит выполнение. По окончании работы EndDialog LookupHostDialogProc вернет Windows значение true, как показано ниже:

case WM_COMMAND: switch (wParam) 

{ 

case IDCANCEL: EndDialog(hwndDlg, FALSE); 

return(TRUE);

Весь процесс диалога состоит из следующих шагов:

  1. Функция LookupHostDialog вызывает функцию DialogBox, которая образует новое диалоговое окно IDD_TEXT. При этом в процессе вызова DialogBox функция LookupHostDialogProc становится функцией обратного вызова для диалогового окна.
  2. Все сообщения Windows от диалогового окна IDD_TEXT посылаютсяфункции LookupHostDialogProc.
  3. Если пользователь закрывает диалоговое окно IDD_TEXT, посылаетсясообщение и функция LookupHostDialogProc вызывает функцию EndDialog спараметром, который возвратится в функцию LookupHostDialog от функции DialogBox.
  4. Если функция LookupHostDialogProc получит сообщение WM_COMMAND с параметром, равным IDOK, она передаст функции EndDialog параметр true. Если функция LookupHostDialogProc получит сообщение WM_COMMAND с параметром IDCANCEL, она передаст функции EndDialog параметр false. Другими словами, EndDialog сообщает, какая из кнопок была нажата пользователем.
  5. Функция DialogBox возвращает значение (true или false), переданное функцией LookupHostDialogProc при вызове функции EndDialog.
  6. Windows уничтожает диалоговое окно IDD_TEXT.

Если окно закрывается при помощи меню Control, функция LookupHostDialogProc получит сообщение WM_CLOSE. Далее это сообщение обрабатывается так, как если бы пользователь нажал на кнопку Cancel. Для этого используется функция Windows PostMessage, посылающая сообщение WM_COMMAND с параметром IDCANCEL самой себе:

case WM_CLOSE: PostMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0L); 

return(TRUE);

В данном случае Windows пошлет сообщение диалогового окна WM_COMMAND с параметром IDCANCEL. Сообщение попадет в функцию LookupHostDialogProc и обработается, как мы только что описали. Другими словами, функция LookupHostDialogProc генерирует сообщение самой себе, в точности повторяя сообщение, посылаемое при нажатии на кнопку Cancel. Если пользователь нажал на кнопку OK, параметр сообщения WM_COMMAND будет равен IDOK, как указано в описании в файле SOCKMAN2.RC. Получив такое сообщение, функция LookupHostDialogProc вызывает функцию Windows GetDlgItem, чтобы извлечь текст, введенный пользователем в диалоговом окне. Далее LookupHostDialogProc помещает этот текст в глобальную переменную szTexpItem.

case WM_COMMAND: switch (wParam) 

{ 

case IDOK: GetDlgItemText(hwndDlg, IDC_TEXTBOX, (LPSTR)szLookupText, 

MAX_HOST_NAME);

EndDialog(hwndDlg, TRUE); 

return(TRUE);

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

11.9 Возвращаемся к функции DoWinsockProgram

Как вам уже известно, когда окно IDD_TEXT закрывается, функция LookupHostDialog возвращает либо значение NULL, либо указатель на данные, введенные пользователем (они хранятся в переменной szLookupText). Если указатель не равен NULL, программа выясняет, чему равно значение переменной wParam, то есть какого рода операция требуется пользователю— блокирующая или не блокирующая:

if (LookupHostDialog()) 

{ 

if (wParam == IDM_LOOKUP_ASYNC) 

hAsyncLookupTask = LookupHostAsync(szLookupText, szLookupBuffer, 

(LPDWORD)&wLookupAddr); 

else 

LookupHostBlocking(szLookupText, szLookupBuffer, TASK_BLOCK_LOOKUP); 

}

Когда Windows посылает сообщение в ответ на выбор пункта меню пользователем, выбранный пункт попадает в переменную wParam. Возможные значения wParam определены в файле SOCKMAN2.RC. Например, пункты меню для операций поиска в DNS определены так:

POPUP "&Lookup Host..."

BEGIN MENUITEM "&Async Lookup", 

IDM_LOOKUP_ASYNC MENUITEM "&Blocking Lookup", 

IDM_LOOKUP_BLOCKINGEND

Если пользователь выбрал пункт подменю Async Lookup, генерируется сообщение WM_COMMAND с параметром wParam, равным IDM_LOOKUP_ASYNC. Если выбрана блокирующая функция (Blocking Lookup), также генерируется сообщение WM_COMMAND, но в этом случае параметр wParam равен IDM_LOOKUP_BLOCKING. Когда функция DoWinsockProgram получает сообщение WM_COMMAND с параметром IDM_LOOKUP_BLOCKING и результат функции LookupHostDialog не равен NULL, вызывается блокирующая функция LookupHostBlocking:

Lookup HostBlocking(szLookupText, szLookupBuffer, TASK_BLOCK_LOOKUP);

11.10 Блокирующий поиск в DNS

Функция LookupHostBlocking похожа на аналогичную из программы QLookup, рассмотренную в девятой главе. Правда, в данном случае вместо наперед заданных значений имени и адреса компьютера используются значения, введенные пользователем. Вот исходный текст функции LookupHostBlocking:

LPHOSTENT LookupHostBlocking(LPSTR szUserEntry, 

LPSTR szHostEntryBuffer, HTASK hTask) 

{ 

LPARAM lParam; 

// Параметр сообщения Windows с 

// сообщениями об ошибках 

DWORD dwIPAddr; 

// IP-адрес в виде беззнакового

// двойного слова 

LPHOSTENT lpHostEntry; 

// Указатель на структуру данных

// с информацией о сетевом

// компьютере

 lpHostEntry = NULL;

  // Предположим, что введен адрес в формате "десятичное с 

// точкой" и попробуем его преобразовать 

if( (dwIPAddr = inet_addr(szUserEntry)) == INADDR_NONE) 

{ 

// Это не был формат "десятичное с точкой" 

// Предположим, что это имя сетевого компьютера 

if ((lpHostEntry = gethostbyname

(szUserEntry)) == NULL) 

{ 

MessageBeep(0); MessageBox(NULL, "Could not get host name.",  

szUserEntry, MB_OK|MB_ICONSTOP); 

} 

} 

else // Преобразуем IP-адрес 

{ 

if ((lpHostEntry = gethostbyaddr((LPCSTR) &dwIPAddr, AF_INET_LENGTH, 

AF_INET)) == NULL) 

{ 

MessageBeep(0);

MessageBox(NULL, "Could not get IP address.", szUserEntry, 

MB_OK|MB_ICONSTOP); 

} 

} 

// Если преобразование успешно завершено, копируем 

// структуру данных с информацией о сетевом компьютере в 

// глобальную переменную szHostEntryBuffer. Это 

// необходимо сделать до того,как вызвать любую 

// другую функцию Winsock

 if (lpHostEntry) 

{ 

memcpy(szHostEntryBuffer, (LPSTR)lpHostEntry, sizeof(HOSTENT)); 

// Нулевое значение переменной lParam говорит об 

// отсутствии ошибок lParam = 0L; 

} 

else  

// Получаем значение ошибки и помещаем его в старшую

// половину слова lParam 

lParam = MAKELONG(0, 

WSAGetLastError());

 // Посылаем сообщение об окончании блокирующего поиска в DNS 

SendMessage(hwndSockman, WM_BLOCK_LOOKUP_DONE, hTask, lParam);

 // Если все прошло успешно, возвращаем указатель на 

// структуру данных с информацией о сетевом компьютере. 

// Если нет— NULL 

return((lpHostEntry ? 

(LPHOSTENT)szHostEntryBuffer : (LPHOSTENT)NULL)); 

}

Как видим, у функции LookupHostBlocking есть три параметра. Первый является указателем на данные пользователя из диалогового окна IDD_TEXT. Второй— указатель на глобальную переменную, хранящую данные об удаленном компьютере. Последний, третий параметр— дескриптор задачи Windows, который функция LookupHostBlocking использует, чтобы идентифицировать себя в сообщении Windows, посылаемом при помощи функции SendMessage.

Для преобразования адреса в формате «десятичное с точкой» или имени компьютера в его IP-адрес, функция LookupHostBlocking выполняет следующие действия:

  1. Функция предполагает, что первый параметр является адресом в формате «десятичное с точкой», и пытается преобразовать его в IP-адрес при помощи функции inet_addr.
  2. Если преобразовать данные не удалось, LookupHostBlocking рассматривает их как имя компьютера и пытается преобразовать его при помощи функции gethostbyname.
  3. Если первый параметр был адресом в формате «десятичное с точкой» и функция inet_addr закончилась успешно, LookupHostBlocking вызывает gethostbyaddr, чтобы получить имя компьютера.
  4. Функция LookupHostBlocking пользуется указателем (lpHostEntry) для хранения полученного от DNS адреса (то есть результата либо gethostbyname, либо gethostbyaddr). Потом LookupHostBlocking проверяет значение указателя.
  5. Если указатель lpHostEntry действителен, функция LookupHostBlocking копирует содержимое буфера по этому указателю в глобальную переменную, на которую указывает ее второй параметр (в нашем случае szLookupBuffer).
  6. После того как сетевые операции закончены (успешно или не успешно), LookupHostBlocking генерирует сообщение Windows, сигнализируя об окончании работы. Переменная lParam служит для передачи информации об ошибках в составе сообщения Windows.
  7. Если функции LookupHostBlocking удалось получить IP-адрес компьютера, она возвращает указатель на буфер, где этот адрес хранится (в виде структуры с информацией о сетевом компьютере). Если получить IP-адрес не удается, функция возвращает указатель NULL.

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

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

В функции LookupHostBlocking асинхронные задания не используются, поэтому дескриптор задания на самом деле не генерируется. Зато вызывается функция SendMessage, посылающая сообщение, аналогичное «настоящему», генерируемому при работе асинхронной функции. Переменная hTask эмулирует настоящий дескриптор задания, возвращаемый асинхронной функцией поиска Избыточность в ваших программах

Вы можете спросить, почему функция LookupHostBlocking эмулирует асинхронные сообщения и зачем, вообще, в программе Sockman есть два варианта программы поиска в DNS. В отличие от программы QLookup, функция LookupHostBlocking должна взаимодействовать с другими функциями. Вы знаете, что практически каждое приложение Интернет нуждается в услугах по преобразованию адресов сетевых компьютеров. Поэтому если функция поиска в DNS спроектирована правильно, ее можно без труда встроить в любое другое приложение или утилиту в составе Sockman. Именно поэтому, функция LookupHostBlocking ведет себя подобно своему асинхронному аналогу, LookupHostAsync. Начинка этих функций совершенно разная, но они обе выдают одинаковые результаты. Поэтому приложению Sockman все равно, какую из этих двух функций ему использовать.

Асинхронные функции Windows должны применяться везде, где только возможно. Они, бесспорно, теснее интегрированы с ориентированной на сообщения средой Windows. Правда, в некоторых реализациях Winsock DLL асинхронные функции поиска в DNS ведут себя неприемлемо плохо. Реализовав два варианта (блокирующий и асинхронный) функции для поиска в DNS, вы обеспечили себе возможный путь для отступления. По крайней мере одна из этих функций будетработать в любом случае. Для начала вызывается асинхронная функция LookupHostAsync. Если она завершается неудачно, следующей вызывается функция LookupHostBlocking. Поскольку результат выдается обеими функциями в одинаковом формате, для вызова одной можно использовать те же самые операторы, что и для вызова другой. Таким образом, конструируя надежный модуль преобразования адресов, вы делаете первый шаг в построении сложного сетевого приложения.

Внесение избыточности в обыкновенные приложения затруднительно, да и не всегда нужно. Но если вы разрабатываете критическое бизнес-приложение Интернет, безусловно, без дублирования некоторых функций уже не обойтись. Просто подумайте о том, что все ваши программы окажутся бесполезными и ничего не смогут сделать до тех пор, пока блок преобразования адресов не получит из DNS IP-адрес сетевого компьютера.

11.11 Асинхронный поиск в DNS

Когда пользователь выбирает пункт подменю Async Lookup, Windows генерирует сообщение WM_COMMAND с параметром wParam, равным IDM_LOOKUP_ASYNC. Когда функция DoWinsockProgram получает это сообщение и результат функции LookupHostDialog не равен NULL, вызывается функция LookupHostAsync, как продемонстрировано ниже:

if (LookupHostDialog()) 

{ 

if (wParam == IDM_LOOKUP_ASYNC) 

hAsyncLookupTask = LookupHostAsync(szLookupText,szLookupBuffer, 

(LPDWORD)&dwLookupAddr);

 else 

LookupHostBlocking(szLookupText, szLookupBuffer, TASK_BLOCK_LOOKUP); 

}

Первые два параметра функции LookupHostAsync те же, что и у функции LookupHostBlocking. Первый параметр, szLookupText, указывает на глобальную переменную, в которой хранится введенная пользователем в диалоговом окне IDD_TEXT информация. Второй параметр, szLookupBuffer, указывает на глобальную переменную, в которой хранится структура данных с информацией о сетевом компьютере. Третий параметр, dwLookupAddr, является глобальной переменной для хранения IP-адреса в двоичном виде. Функция LookupHostAsync состоит из следующих операторов:

HTASK LookupHostAsync(LPSTR szUserEntry, LPSTR szHostEntryBuffer, 

LPDWORD lpdwAddr) 

{ 

HTASK hTask; 

// Дескриптор асинхронного задания

 // Предполагаем, что адрес в формате

// "десятичное с точкой"и пробуем

// преобразовать 

if ((*lpdwAddr = inet_addr

(szUserEntry)) == INADDR_NONE) 

{ 

lstrcpy(szHostName, szUserEntry); 

// Записываем имя  

// сетевого компьютера 

hTask = WSAAsyncGetHostByName(hwndSockman, WM_ASYNC_LOOKUP_DONE, 

szUserEntry, szHostEntryBuffer, MAXGETHOSTSTRUCT); 

} 

else 

{ 

lstrcpy(szIPAddress, szUserEntry); 

// Записываем имя 

// сетевого компьютера 

hTask = WSAAsyncGetHostByAddr(hwndSockman, WM_ASYNC_LOOKUP_DONE, 

(LPCSTR)&lpdwAddr, AF_INET_LENGTH, AF_INET, szHostEntryBuffer, 

MAXGETHOSTSTRUCT); 

} 

return(hTask); 

// Возвращаем дескриптор задания 

}

Так же, как и функция LookupHostBlocking, функция LookupHostAsync вначале предполагает, что пользователь ввел адрес компьютера в формате «десятичное с точкой» и пытается преобразовать его, вызывая функцию inet_addr. Если преобразование оказалось неудачным, LookupHostAsync пробует вызвать функцию gethostbyname. В любом случае значение переменной szUserEntry копируется в глобальную переменную.

Если пользователь ввел адрес формата «десятичное с точкой», адрес заносится в переменную szIPAddress. Если введенные данные— имя компьютера, имя заносится в переменную szHostName. Далее эти значения используются другими модулями Sockman. Например, когда вызывается Finger, Sockman предлагает указать в качестве сетевого компьютера имя последнего из найденных в DNS.

В функции LookupHostAsync используются две новых асинхронных функции Winsock. Вместо gethostbyname используется WSAAsyncGetHostByName, а вместо gethostbyaddr— WSAAsyncGetHostByAddr.

11.12 Асинхронные функции Winsock

Ниже приведен прототип функции WSAAsyncGetHostByAddr. На первый взгляд, она кажется сложнее своего аналога из интерфейса Беркли:

HANDLE PASCAL FAR WSAAsyncGetHostByAddr(HWND hWnd,unsigned int wMsg, 

const char FAR * addr, int len, int type, char FAR * buf, int buflen);

У функции WSAAsyncGetHostByAddr семь параметров, на четыре больше, чем у gethostbyaddr. Три из параметров WSAAsyncGetHostByAddr в точности соответствуют параметрам gethostbyaddr. Вот прототип функции gethostbyaddr:

struct hostent FAR * PASCAL FAR gethostbyaddr(const char FAR * addr, 

int len, int type);

У обеих функций есть одинаковые параметры:

const char FAR * addr; 

// Указатель на IP-адрес с сетевым

// порядком байтов.int len; 

// Длина адреса. В случае Интернет

// всегда равна 4 байтам.int type; 

// Тип адреса. Для Интернет всегда AF_INET.

Главное отличие между WSAAsyncGetHostByAddr и gethostbyaddr в типе возвращаемого результата. gethostbyaddr возвращает указатель на структуру данных с информацией о сетевом компьютере (host-entry), а WSAAsyncGetHostByAddr— дескриптор задания Windows. Ключевой момент в понимании работыWSAAsyncGetHostByAddr— знать, что делать дальше с этим дескриптором.

11.13 Что такое дескриптор асинхронного задания Windows?

Дополнительно к обыкновенным параметрам функций Winsock в стиле Беркли, их асинхронным аналогам требуются два специальных параметра. Первый параметр— дескриптор окна Windows, которому посылается сообщение об окончании работы функции. Второй— само посылаемое сообщение. Как только асинхронная функция отработает, указанное сообщение отсылается заданному окну. Например, функция LookupHostAsync вызывает функцию WSAAsyncGetHostByName следующим образом:

hTask = WSAAsyncGetHostByName(hwndSockman,

hTask = WSAAWM_ASYNC_LOOKUP_DONE,hTask = WSAAszUserEntry, 

szHostEntryBuffer, MAXGETHOSTSTRUCT);

Функция WSAAsyncGetHostByName выполняет задачу по преобразованию имени в IP-адрес, используя информацию из параметра szUserEntry. Функция WSAAsyncGetHostByName возвращает управление вызывавшему модулю сразу, как только задача запущена на выполнение. Она не ждет, пока операция поиска в DNS закончится. Поскольку на момент возврата функции WSAAsyncGetHostByName результат еще не получен, она не может вернуть указатель на структуру с информацией о сетевом компьютере, как это делает gethostbyname. Вместо этого WSAAsyncGetHostByName возвращает дескриптор задачи Windows, идентифицирующий процесс преобразования имени в IP-адрес. Дескриптор заносится в переменную hTask.

Как только сетевая операция закончится, Windows пошлет сообщение окну сдескриптором hwndSockman. Идентификатором этого сообщения будет WM_ASYNC_LOOKUP_DONE. Другими словами, первые два параметра функции WSAAsyncGetHostByName определяют, кому и что послать по завершении асинхронной задачи. Асинхронные функции Winsock начинают операциии немедленно возвращают управление вызывавшему модулю. Результатом большинства асинхронных функций является дескриптор задачи Windows. Дескриптор задачи однозначно идентифицирует задачу, запущенную асинхронной функцией, и в дальнейшем используется для идентификации сообщений, генерируемых асинхронной задачей.

11.14 Параметры асинхронного сообщения Windows

Все сообщения Windows имеют определенный формат. Каждое сообщениесостоит из четырех переменных: дескриптора окна-получателя сообщения, идентификатора сообщения, 16-битного и 32-битного параметров сообщения. Первый параметр сообщения часто называется wParam (word parameter). Второй параметр часто называется lParam (long parameter).

Многие Windows-сообщения не используют оба параметра для хранения сколь-нибудь полезной информации. Однако это не относится к сообщениям функций Winsock. Параметр wParam сообщения Winsock содержит дескриптор асинхронного задания, который вернула функция, запустившая это задание. Если старшие 16 бит переменной lParam не равны нулю, то в них содержится код ошибки. В следующем разделе разъясняется смысл младших 16 бит переменной lParam.

11.15 Ошибки асинхронных функций Winsock

При вызове большинства асинхронных функций Winsock требуется задавать указатель на буфер для хранения данных, равно как и его длину. Как правило, два этих аргумента— последние в списке параметров. Разработчики Winsock предусмотрели возможность возникновения ошибки, связанной с нехваткой места в буфере для принятых сетевых данных. Младшие 16 бит переменной lParam служат именно для этих целей. Предположим, что количество принятых сетевых данных превысило отведенный для них размер буфера, который вы задали при вызове функции. Вместо того чтобы превысить допустимый объем буфера или неудачно закончиться, функция посылает сообщение с кодом ошибки WSANOBUFS. Код ошибки WSANOBUFS попадает в старшие 16 бит переменной lParam. Ошибка означает, что либо вы не отвели места для буфера, принимающего данные, либо этого места недостаточно.

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

Динамическое распределение памяти
Если размер отведенного вашей программой буфера слишком мал, чтобы вместить все данные, функция возвращает значение ошибки WSANOBUFS в двух старших байтах переменной lParam. В двух младших байтах lParam находится требуемый размер буфера. Рассмотрим эту ситуацию на практике. Если прикладная программа работает в условиях нехватки памяти, память для буфера можно отводить динамически. Например, в вызове функции можно указать размер буфера, равный нулю, получить сообщение об ошибке WSANOBUFS и выделить необходимую память динамически.
Для этого программа должна проанализировать два младших байта переменной lParam сообщения Windows. Определив размер буфера, программа динамически отводит память и вызывает асинхронную функцию снова, но уже с правильным размером буфера. Поступая таким образом, программа никогда не отнимет лишней памяти у системы. Конечно, такому подходу свойственны и определенные недостатки. Прежде всего страдает производительность— ведь одну и ту же функцию приходится вызывать два раза вместо одного. Разработчик должен выбрать, что важнее— потребление памяти или скорость работы системы.

Для получения кода ошибки и требуемого размера буфера из параметра lParam сообщения Winsock в сокетах Windows предусмотрены два макроса. Макрос WSAGETASYNCERROR возвращает код ошибки— содержимое двух старших байтов переменной lParam, а макрос WSAGETASYNCBUFLEN— требуемую длину буфера данных— содержимое двух младших байтов переменной lParam. Макросы используются следующим образом:

UINT nErrorNumber = WSAGETASYNCERROR(lParam);

UINT nRequiredBuf = WSAGETASYNCBUFLEN(lParam);

11.16 Возвращаясь к функции DoWinsockProgram

Функция LookupHostAsync, как вы помните, возвращает дескриптор задачи Windows (функции WSAAsyncGetHostByName или функции WSAAsyncGetHostByAddr). Как показано ниже, функция DoWinsockProgram заносит значение этого дескриптора в глобальную переменную hAsyncLookupTask:

if (wParam == IDM_LOOKUP_ASYNC) hAsyncLookupTask = LookupHostAsync

(szLookupText, szLookupBuffer, (LPDWORD)&wLookupAddr);

С этого момента ответственность за происходящее переходит к Windows. Другими словами, в ответ на выбор пользователем подпункта меню Async Lookup Sockman произвел все необходимые действия для запуска асинхронной задачи поиска в системе DNS. Эти действия таковы:

  1. Sockman вывел на экран диалоговое окно и позволил пользователю ввести имя или адрес (в формате «десятичное с точкой») компьютера.
  2. Sockman передал введенные пользователем данные функции LookupHostAsync.
  3. Функция LookupHostAsync запустила задачу по асинхронному поиску в DNS.
  4. Sockman записал значение дескриптора задачи в глобальную переменную hAsyncLookupTask. (Глобальной эта переменная сделана потому, что это облегчает доступ остальных приложений к сообщениям задачи асинхронного поиска в DNS.)

Как только задача асинхронного поиска в DNS закончится, Windows пошлет сообщение. Вот как осуществляется вызов асинхронных функций Winsock в функции LookupHostAsync:

hTask = WSAAsync GetHostByName(hwndSockman, WM_ASYNC_LOOKUP_DONE, 

szUserEntry, szHostEntryBuffer, MAXGETHOSTSTRUCT);

hTask = WSAAsyncGetHostByAddr(hwndSockman, WM_ASYNC_LOOKUP_DONE, 

(LPCSTR)&lpdwAddr,AF_INET_LENGTH, AF_INET, szHostEntryBuffer, 

MAXGETHOSTSTRUCT);

Обратите внимание, что обе функции (WSAAsyncGetHostByName и WSAAsyncGetHostByAddr) имеют одинаковые параметры— дескриптор окна, равный hwndSockman и идентификатор сообщения, равный WM_ASYNC_LOOKUP_DONE. Дескриптор окна hwndSockman идентифицирует окно Sockman. Сообщение WM_ASYNC_LOOKUP_DONE высылается окну Sockman по окончании работы функции. Другими словами, его получит обработчик сообщений окна Sockman— функция WndProc.

11.17 Модифицируем функцию WndProc

Обе функции, LookupHostAsync и LookupHostBlocking, запускают задачу поиска в DNS и заставляют Windows послать сообщение по окончании этой задачи. Сообщение посылается процедуре WndProc, являющейся обработчиком всех сообщений, адресованных главному окну Sockman.

Как только задача поиска в DNS, запущенная функцией LookupHostAsync, заканчивается, Windows посылает сообщение WM_ASYNC_LOOKUP_DONE функции WndProc. Как только функция LookupHostBlocking заканчивается, Windows посылает сообщение WM_BLOCK_LOOKUP_DONE функции WndProc. Эти сообщения обрабатываются операторами case, как показано во фрагменте исходного текста функции WndProc из файла SOCKMAN2.CPP:

long FAR PASCAL _export WndProc(HWND hwnd, UINT iMessage, 

UINT wParam, LONG lParam) 

{ 

switch (iMessage) 

{ 

case WM_ASYNC_LOOKUP_DONE: 

case WM_BLOCK_LOOKUP_DONE: 

DisplayHostEntry(hwnd, iMessage, wParam, lParam); 

return(0); 

// Закончили

Как видим, оба сообщения вызывают функцию DisplayHostEntry. Если результаты работы функций поиска в DNS требуются другим модулям программы, эти операторы следует изменить. В данном случае на экран выводится окно-сообщение с этими результатами.

11.18 Функция DisplayHostEntry

Функция DisplayHostEntry, как следует из названия, выводит результатыпоиска в DNS на экран компьютера. При этом она проверяет наличие сообщенийоб ошибках от асинхронных функций при помощи макроса WSAGETASYNCERROR.

Примечание:Обратите внимание на то, что функция LookupHostBlocking эмулирует асинхронное сообщение об ошибке, вызывая функцию SendMessage с установленной переменной lParam. Получается, что DisplayHostEntry проверяет ошибки как асинхронной, так и блокирующей функции.

VOID DisplayHostEntry(HWND hwnd, UINT iMessage, UINT wParam, 

LONG lParam) 

{ 

int nErrCode; 

// Код ошибки от преобразователя адресов

 if (nErrCode = WSAGETASYNCERROR(lParam)) { wsprintf(szScratchBuffer, 

"%s LOOKUP caused Winsock ERROR No. %d",szLookupText, nErrCode); 

MessageBeep(0); 

MessageBox(NULL, szScratchBuffer, szLookupText,MB_OK|MB_ICONSTOP); 

// Если произошла ошибка, буфер обнуляется 

szLookupBuffer[0] = '\0'; 

} 

else 

{ 

PHOSTENT pHostEntry;

pHostEntry = (PHOSTENT) szLookupBuffer; 

lstrcpy(szHostName, pHostEntry->h_name); lstrcpy(szIPAddress, 

inet_ntoa(*(PIN_ADDR)*(pHostEntry->h_addr_list))); 

wsprintf(szScratchBuffer, 

"%s\tLOOKUP RESULTS\nHost Name:\t%s\nIP Address:\t%s", 

szLookupText,szHostName, szIPAddress); 

} 

PaintWindow(szScratchBuffer); 

return; 

}

Если содержимое lParam, как показывает вызов WSAGETASYNCERROR, свидетельствует об ошибке, функция DisplayHostEntry выводит сообщение об ошибке и устанавливает нулевую длину буферу szLookupBuffer. Если ошибки не было, выводится сообщение с результатами, взятыми из структуры данных host-entry (с информацией о сетевом компьютере), записанной в szLookupBuffer, приведенной предварительно к типу PHOSTENT (указатель на структуру host-entry). Поле h_name структуры host-entry копируется в глобальную переменную szHostName. Имя сетевого компьютера, записанное в szHostName, может использоваться остальными программными модулями Sockman, как имя компьютера по умолчанию.

Для преобразования элемента h_addr_list из формата структуры host-entry в простой ASCII-текст, используется функция inet_ntoa. Далее, эта строка копируется в глобальную переменную szIPAddress. Опять-таки, последний извлеченный из DNS адрес может использоваться другими приложениями Sockman по умолчанию. После того как адрес и имя компьютера сохранены, DiplayHostEntry при помощи функции wsprintf формирует строку результата в буфере общего назначения szScratchBuffer. Указатель на этот буфер передается функции PaintWindow, которая и выводит результаты поиска в DNS в главное окно Sockman.

Подводя итоги

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

В следующей главе вы узнаете, как простая утилита Finger, рассмотренная в десятой главе, интегрируется в настоящее Windows-приложение. Также вы узнаете, как выглядит асинхронная версия той же самой процедуры. До того как перейти к двенадцатой главе, проверьте, хорошо ли вы усвоили следующие важные понятия:

  • При вызове асинхронной функции сетевого ввода-вывода Winsock, как правило, задается указатель на буфер данных и размер этого буфера.
  • Асинхронные функции ввода-вывода Winsock, как правило, возвращают дескриптор задания Windows.
  • Сообщение от асинхронной функции Windows содержит 16-битный параметр wParam, в котором находится дескриптор задания.
  • Сообщение об ошибке асинхронной функции хранится в старших шестнадцати битах 32-битного параметра lParam.
  • Если размеров буфера при вызове асинхронной функции не хватает, чтобы вместить все данные, генерируется сообщение об ошибке WSANOBUFS (размер буфера слишком мал), а необходимый размер буфера извлекается программой при помощи макроса WSAGETASYNCBUFLEN из 16 младших бит 32-битного параметра сообщения lParam.
Далее   Вверх   Содержание

ПОМОЩЬ

Оплата и доставка
Вопросы и ответы
Карта сайта
Обратная связь






ПОДПИСКА

Хотите быть в курсе событий?
Подпишитесь на еженедельную рассылку Издательского дома «Питер» о новинках, акциях, скидках. подробнее


ЗАКАЗ КНИГ
ПО ТЕЛЕФОНУ

                  (812) 703-73-74              

piter.com ЗАКАЗ КНИГ
ПО ICQ


413763617   

Заказ книг Skype ЗАКАЗ КНИГ
ПО SKYPE


piter_dot_com



ГЛАС ЧИТАТЕЛЯ
отзыв на книгу:
Шерше ля нефть. Почему наш Стабилизационный фонд находится ТАМ?
Отличная книга! Зашел в книжный магазин за книгой в дорогу. "Шерше ля нефть..." оказалась - второй книгой, попавшейся на глаза. Прочитав аннотацию сразу взял. В восторге от книги, все сомнения по поводу демократии, либералов и дружеских отношений с Западом отпали. Автору спасибо.
Иван


ПАРТНЕРЫ

 

    Главная | Новинки | Новости | Рейтинг продаж | Файлы | Клуб Профессионал | Партнерская программа

Авторские права охраняются.
Воспроизведение материалов или их частей в любом виде без письменного разрешения запрещено!
© 1997-2009, Издательский дом «Питер»

  [AD]   [AD]      
Санкт-Петербург
Б. Сампсониевский пр., 29а
тел.: (812) 703-73-74,
(812) 703-73-73
e-mail: postbook@piter.com
sales@piter.com
http://www.piter.com