Программирование в сетях Windows. Э. Джонс, Д. Оланд
Скачать 2.88 Mb.
|
// nPortld к сетевому порядку и присвоение результата sm_port. InternetAddr.sin_port = htons(nPortld); Теперь подготовим сокет для соединения по TCP или UDP. Создание сокета Создание IP-сокета позволит приложениям осуществлять подключение через TCP, UDP и протоколы IP Для открытия IP-сокета при помощи протокола TCP, вызовите функцию socket или WSASocket с семейством адресов AFJNET и ти- пом сокета SOCK_STREAM, а также присвойте значение 0 полю протокола s = socket(AF_INET, SOCK_STREAM, 0), s = WSASocket(AF_INET, SOCK.STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED), Чтобы открыть IP-сокет при помощи протокола UDP, вместо SOCKSTREAM укажите тип сокета SOCKDGRAM в функции socket или WSASocket Также мож- но открывать сокет для связи непосредственно по IP — для этого задайте тип сокета SOCK_RAW Подробнее об этом параметре — в главе 13 Разрешение имен Для подключения к узлу по IP Winsock-приложение должно знать IP-адрес этого узла, сложный для запоминания пользователем Большинство людей более охотно обращаются к компьютерам при помощи легко запоминаемых имен узлов, а не IP-адресов В Winsock предусмотрено две функции для раз- Решения имени в IP-адрес 1 2 6 ЧАСТЬ II Интерфейс прикладного программирования Winsock Функции gethostbyname и WSAAsyncGetHostByName отыскивают в базе дан- ных узла сведения об узле, соответствующие его имени Обе функции воз- вращают структуру HOSTENT struct hostent char FAR * h_name, char FAR • FAR • h.aliases, short h.addrtype, short h_length, char FAR * FAR * h_addrj.ist, Поле hname является официальным именем узла Если в сети использу- ется доменная система имен (Domain Name System, DNS), в качестве имени сервера будет возвращено полное имя домена (Fully Qualified Domain Name, FQDN) Если в сети применяется локальный файл узлов (hosts, lmhosts) — это первая запись после IP-адреса Поле h_ahases — массив, завершающийся ну- лем (null-terminated array) дополнительных имен узла Поле b_add? type пред- ставляет возвращаемое семейство адресов Поле hjength охире-црпяег длину в байтах каждого адреса из поля h_addrjist Поле h_addrjist — массив, за- вершающийся 0 и содержащий IP-адреса узла (Узел может иметь несколько IP-адресов) Каждый адрес в этом массиве представлен в сетевом порядке Обычно приложение использует первый адрес из массива Впрочем, при получении нескольких адресов, приложение должно выбирать адрес случай- ным образом из числа доступных, а не упорно использовать первый API-функция gethostbyname определена так struct hostent FAR * gethostbyname ( const char FAR * name Параметр name представляет дружественное имя искомого узла При ус- пешном выполнении функции возвращается указатель на структуру HOSTENT, которая хранится в системной памяти Приложение не должно полагать, что эти сведения непременно статичны Поскольку эта память обслуживается си- стемой, оно не должно освобождать возвращенную структуру WSAAsyncGetHostByName — асинхронная версия функции gethostbyname, оповещающая приложение о завершении своего выполнения с помощью сообщений Windows HANDLE WSAAsyncGetHostByName( HWND hWnd, unsigned int wMsg, const char FAR * name, char FAR * buf, Int buflen Параметр hWnd — дескриптор окна, которое получит сообщение по за- вершении выполнения асинхронного запроса. Параметр wMsg — Windows- •ГЛАВА 6 Семейства адресов и разрешение имен '127 сообщение, которое будет возвращено по завершении выполнения асинх- ронного запроса Параметр пате— дружественное имя искомого узла Па- раметр buf— указатель на область данных, куда помещается HOSTENT Этот буфер должен быть больше структуры HOSTENT и иметь размер, определен- ный BMAXGETHOSTSTRUCT Стоит упомянуть еще две функции поиска сведений об узле gethostbyaddr и WSAAsyncGetHostByAddr Они полезны, когда вы знаете IP-адрес узла и хотите найти его понятное пользователю имя Функция gethostbyaddr определена так struct HOSTENT FAR • gethostbyaddr( const char FAR • addr, int len, mt type ), Параметр addr — указатель на IP-адрес в сетевом порядке Параметр len задает длину параметра addr в байтах Параметр type должен иметь значение AFJNET (IP-адрес) WSAAsyncGetHostByAddr — асинхронная версия функции gethostbyaddr Номера портов Помимо IP-адреса удаленного компьютера для подключения к службе на ло- кальном или удаленном компьютере приложение должно знать номер пор- та службы При использовании TCP и UDP приложение решает, через какой порт связаться Существуют стандартные номера портов, зарезервированные для служб сервера, поддерживающих протоколы более высокого уровня, чем TCP Например, порт 21 зарезервирован для FTP, 80 — для HTTP Как уже упо- миналось, стандартные службы обычно используют порты 1-1023 Поэто- му если вы разрабатываете TCP-приложение, которое не использует ни одну из известных служб, старайтесь задействовать порты с номером больше 1023 Вы можете узнать номера портов, используемых стандартными служ- бами, вызвав функцию getservbyname или WSAAsyncGetServByName Эти фун- кции просто извлекают статическую информацию из файла с именем ser- vices В Windows 95 и Windows 98 файл служб расположен в папке %WIN- DOWS%, а в Windows NT и Windows 2000 - в %WINDOWS%\System32\Dn- vers\Etc Функция getservbyname определена так struct servant const const char char FAR FAR FAR * getservbyname( * name, * proto Параметр name представляет имя искомой службы Например, если вы пы- таетесь найти порт, используемый FTP, присвойте параметру пате указатель на строку «ftp» Параметр proto иногда ссылается на строку, указывающую прото- кол, под которым зарегистрирована служба из параметра пате Функция WSA- •AsyncGetServByName — асинхронная версия getsen b)>name В Windows 2000 применен новый динамический метод регистрации и зап- роса информации о службах для TCP и UDP Серверные приложения могут за- 1 28 ЧАСТЬ II Интерфейс прикладного программирования Winsock регистрировать имя службы, IP-адрес и номер порта службы при помощи функ- ции WSASetSert'ice Клиентские приложения запрашивают информацию об этих службах при помощи комбинации API-функций WSALookupSetviceBegin, WSA- LookupServiceNext и WSALookupServtceEnd (см также главу 10) Инфракрасные сокеты Сокеты инфракрасного канала (Infrared sockets, IrSock) — новая интерес- ная технология, впервые реализованная в Windows СЕ Они позволяют под- ключаться двум компьютерам по инфракрасному последовательному порту Инфракрасная связь теперь доступна в Windows 98 и в Windows 2000 Инф- ракрасные сокеты отличаются от традиционных тем, что учитывают непо- стоянство доступности переносных устройств В этой технологии примене- на новая модель разрешения имен Адресация Поскольку большинство компьютеров с устройствами инфракрасной свя- зи (Infrared Data Association, IrDA) мобильны, традиционные схемы разреше- ния имен не эффективны Обычные статические ресурсы, такие как серве- ры имен, бесполезны, когда сетевой клиент перемещается по сети и за ее пределы Для решения этой проблемы IrSock ищет ресурсы в радиусе связи произвольным образом, не налагая нагрузки на всю сеть и не применяя стандартные функции службы имен Winsock или IP-адреса Вместо этого служба имен встроена в поток связи, а для поддержки служб, относящихся к последовательным инфракрасным портам, введено новое семейство адресов Структура адреса IrSock содержит имя службы с описанием приложения, ис- пользуемого в вызовах для привязки и подключения, а также идентифика- тор устройства (device identifier), определяющий устройство, на котором выполняется данная служба Эта пара аналогична IP-адресу и номеру порта, используемым в обычных TCP/IP-сокетах Структура адреса IrSock такова typedef struct sockaddr_irda { u_short lrdaAddressFamily, u_char irdaDeviceID[4], char irdaServiceName[25], > SOCKADDR_IRDA, Поле lrdaAddressFamily всегда равно AFJRDA Параметр trdaDevicelD — четырехсимвольная строка, уникально идентифицирующая устройство, на котором запущена определенная служба Это поле игнорируется при созда- нии IrSock-сервера, но важно для клиента, поскольку указывает на IrDA-ycT- ройство, к которому он подключен (В зоне действия может быть несколько устройств ) И, наконец, поле irdaServiceName — это имя службы, которую приложение регистрирует или к которой пытается подключиться Разрешение имен Адресация может быть основана на селекторах (IrDA Logical Service Access Point Selector, LSAP-SEL) или на службах, зарегистрированных Information X Л A B A 6 Семейства адресов и разрешение имен '129 Access Services (IAS) IAS абстрагирует службу от LSAP-SEL в виде дружествен- ного текстового имени, примерно так же, как DNS-сервер отображает име- на компьютеров в цифровые IP-адреса Для успешного соединения вы може- те использовать как LSAP-SEL, так и дружественное пользователю имя, но в последнем случае требуется разрешать имена Как правило, прямой LSAP-SEL- «адрес > не применяют, поскольку адресное пространство служб IrDA огра- ничено Реализация Win32 разрешает использовать целые идентификаторы LSAP-SEL в диапазоне 1-127 По сути, IAS-сервер можно рассматривать как WINS-сервер, поскольку он ассоциирует LSAP-SEL с текстовым именем службы Фактически в записи IAS для нас важны лишь три поля имя класса (class name), атрибут (attribute) и значение атрибута (attribute value) Допустим, сервер хочет регистрироваться под именем службы MyServer Для этого ему нужно осуществить привязку к соответствующей структуре SOCKADDRJRDA Затем добавляется запись IAS с именем класса MyServer, атрибутом IrDATiny- ТР LsapSel и значением атрибута, например, 3 Это значение атрибута — сле- дующий неиспользуемый LSAP-SEL, назначенный системой после регистра- ции С другой стороны, клиент передает структуру SOCKADDRJRDA вызову подключения В результате IAS начинает искать службу с именем класса MyServer и атрибутом IrDA TinyTP LsapSel Этот IAS-запрос вернет значение 3 Вы можете сформулировать свой IAS-запрос с помощью параметра IRLMP_ IAS_QUERYB вызове getsockopt Если вы хотите полностью проигнорировать IAS (что не рекомендуется), задайте LSAP-SEL-адрес напрямую для имени сервера или конечной точки, с которой хочет соединиться клиент Обходить IAS, требуется только при под- ключении к устаревшим IrDA-устройствам, которые не поддерживают IAS- регистрацию (например, принтерам с инфракрасным портом) Для обхода IAS-регистрации и поиска задайте имя службы в структуре SOCKADDRJRDA как LSAP-SEL-xxx, где ххх — значение атрибута в диапазоне 1-127 В итоге серверу будет явно назначен данный LSAP-SEL-адрес, а клиент, не ведя IAS- поиск, сразу попытается подключиться к любой службе, выполняемой на LSAP-SEL Нумерация lrDA-устройств Поскольку инфракрасные (ИК) устройства появляются и исчезают из ради- уса связи, необходим метод динамического перечисления соседних уст- ройств Реализация этого механизма в Windows СЕ и в Windows 98/2000 не- скольких отличается В Windows СЕ поддержка IrSock появилась раньше, чем в Windows 98 и Windows 2000, но последние в ответ на запрос нумерации возвращают дополнительную «справочную > информацию Поэтому заголо- вочный файл Af_irda h для Windows СЕ содержит исходные минимальные определения структур, а новые заголовочные файлы других платформ — Дополнительные определения для каждой платформы, поддерживающей rbock Для согласованности рекомендуется использовать более поздние вер- сии заголовочного файла Af_irda h Нумерация соседних ИК-устройств выполняется функцией getsockopt с параметром IRLMP_ENUMJDEVICES для Структура DEVICEUST передается как 1 30 ЧАСТЬ II Интерфейс прикладного программирования Winsock параметр optval. Существуют две структуры, одна для Windows 98 и Windows 2000, а другая — для Windows СЕ. Они определены так: typedef struct _WINDOWS_DEVICELIST { ULONG numDevice; WINDOWS_IRDA_DEVICE_INFO Device[1]; } WINDOWS.DEVICELIST, «PWINDOWS.DEVICELIST, FAR *LPWINDOWS_DEVICELIST; typedef struct _WCE_DEVICELIST { ULONG numDevice; WCE_IRDA_DEVICE_INFO Device[1]; } WCE_DEVICELIST, *PWCE_DEVICELIST; Единственное отличие между ними — структура для Windows 98 и Windows 2000 содержит массив структур WINDOWSJRDAJDEVICEJNFO вместо WCE_ IRDAJDEVICEJNFO. Условная директива *deflne объявляет DEVICEUST как со- ответствующую структуру, в зависимости от целевой платформы. Аналогич- но, существует и два объявления структуры IRDA_DEVICE_INFO. typedef struct _WINDOWS_IRDA_DEVICE_INFO { u_char irdaDevlceID[4]; char irdaDevlceName[22]; u_char irdaOeviceHintsi; u_char irdaDeviceHints2; u_char irdaCharSet; } WINDOWS_IRDA_DEVICE_INFO, *PWINDOWS_IRDA_DEVICE_INFO, FAR *LPWINDOWS_IRDA_DEVICE_INFO; typedef struct _WCE_IRDA_OEVICE_INFO { u_char irdaDeviceID[4]; char irdaDeviceName[22]; u_char Reserved[2]; . > ^ } WCE_IRDA_DEVICE_INFO, *PWCE_IRDA_DEVICE_INFO; " f Условная директива *define объявляет IRDAJDEVICEJNFO, опять-таки, в зависимости от платформы. Как мы уже говорили, фактически нумерация ИК-устройств выполняет- ся функцией getsockopt с параметром IRLMP_ENUM_DEVICES. Следующий код перечисляет идентификаторы всех ИК-устройств по соседству. SOCKET sock; DEVICELIST devList; DWORD dwListLen=sizeof(DEVICELIST); sock = WSASocket(AF_IRDA, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED); devList.numDevice = 0; Г Л А В А 6 Семейства адресов и разрешение имен 131 dwRet = getsockopt(sock, SOL_IRLMP, IRLMP_ENUMDEVICES, (char *)&devList, AdwHstLen); Перед тем как структура DEVICELIST будет передана в вызове getsockopt, не забудьте присвоить полю numDevice значение О. При успешной нумерации полю numDevice будет присвоено положительное значение, равное числу структур IRDAJDEVICEJNFO в поле Device. В реальном приложении выпол- нять getsockopt придется неоднократно, чтобы отследить все устройства, попавшие в радиус связи. Например, программа должна пытаться обнару- жить ИК-устройство не менее пяти раз. Для этого просто поместите вызов процедуры в цикл с коротким вызовом функции Sleep после каждой безус- пешной нумерации. Зная, как нумеровать ИК-устройства, создать клиент или сервер не слож- но. Серверная часть немного проще, поскольку выглядит как «обычный» сер- вер, то есть никаких особых действий не требует. Вкратце порядок действия IrSock-сервера таков. 1. Создание сокета с семейством адресов AFJRDA и типом SOCK_STREAM. 2. Запись в структуру SOCKADDRJRDA имени службы сервера. 3. Вызов функции bind с описателем сокета и структурой SOCKADDRJRDA. 4. Вызов функции listen с описателем сокета и тайм-аутом. 5. Блокирование вызова функции accept для входящих запросов клиентов. Действия клиента сложнее, поскольку необходимо нумеровать ИК-уст- ройства. 1. Создание сокета с семейством адресов AFJRDA и типом SOCKSTREAM. 2. Нумерация доступных ИК-устройств путем вызова getsockopt с парамет- ром IRLMP_ENUMJ)EVICES. 3- Для каждого найденного устройства в структуру SOCKADDRJRDA — за- пись идентификатора найденного устройства, а также имени службы, к которой вы хотите подключиться. 4. Вызов функции connect с описателем сокета и структурой SOCKADDRJRDA. Это действие выполняется для каждой структуры, обработанной на шаге 3- Опрос IAS Существует два способа определить, запущена ли данная служба на конкрет- ном устройстве. Первый — попытаться подключиться к службе, другой — зап- росить у IAS имя службы. Оба способа требуют нумерации всех ИК-устройств и попыток запросить каждое устройство, пока одно из них не ответит (или подключиться к нему). Запрос выполняется функцией getsockopt с параметром IRIMPJAS_QUERY. Указатель на структуру IAS_QUERY передается в параметре optval. И снова, существуют две структуры IAS_QUERY: одна — для Windows 98 и Windows 2000, а другая — для Windows СЕ. Вот их описания. typedef struct .WINDOWS.IAS_QUERY u_char irdaDeviceID[4]; '* '*''' 132 ЧАСТЬ II Интерфейс прикладного программирования Winsock char char u_long union i rdaClassName[IAS_MAX_CLASSNAME]; irdaAttribName[IAS_MAX_ATTRIBNAME]; irdaAttribType; LONG irdaAttriblnt; struct { u_long Len; u_char OctetSeq[IAS_MAX_OCTET_STRING]; } irdaAttribOctetSeq; struct u_long Len; u_long CharSet; u_char UsrStr[IAS_MAX_USER_STRING]; } irdaAttribUsrStr; } irdaAttribute; } WINDOWS_IAS_QUERY, *PWINDOWS_IAS_QUERY, FAR «LPWINDOWS.IAS.QUERY; typedef struct _WCE_IAS_QUERY u_char char char u_short union irdaDeviceID[4]; irdaClassName[61]; irdaAttribName[61]; irdaAttribType; int irdaAttriblnt; struct int Len; u_char 0ctetSeq[1]; u_char Reserved[3]; } irdaAttribOctetSeq; struct iTO HKjJ int Len; u_char CharSet; u_char UsrStr[1]; u_char Reserved[2]; } irdaAttribUsrStr; |