Программирование в сетях Windows. Э. Джонс, Д. Оланд
Скачать 2.88 Mb.
|
Листинг 1-4. Клиент на основе асинхронных событий (Nbclient.c) // Nbclient с ((include «include ((include Г Л А В А 1 Интерфейс NetBIOS 31 Листинг 1 -4. (продолжение) «include \Common\nbcommon h «define MAX_SESSIONS 254 «define MAX_NAMES 254 «define MAX_BUFFER 1024 char szServerName[NCBNAMSZ]; // // функция Connect // // Описание // Установка асинхронного соединения с сервером на данном LANA // В переданной структуре NCB полю ncb_event уже присвоено значение // действительного описателя события Windows Просто // заполните пробелы и сделайте вызов // int Connect(PNCB pncb, mt lana, char *server, char «client) { pncb->ncb_command = NCBCALL | ASYNCH, pncb->ncb_lana_num = lana, memset(pncb->ncb_name, , NCBNAMSZ), strncpy(pncb->ncb_name, client, strlen(client)); memset(pncb->ncb_callname, , NCBNAMSZ), strncpy(pncb->ncb_callname, server, strlen(server)); if (Netbios(pncb) != NRC_GOODRET) { pnntf( ERROR Netbios NCBCONNECT Xd\n", pncb->ncb_retcode), return pncb->ncb_retcode, } return NRCJ3OODRET, // Функция main // // Описание // Инициализирует интерфейс NetBIOS, распределяет некоторые ресурсы // (описатели событий, буфер отправки и т п ) и дает команду // NCBCALL для каждого номера LANA на указанном сервере Когда соединение // создано, отменяет или разрывает любые неудавшиеся // соединения Затем посылает/получает данные Наконец, выполняет очистку ян >uki &* см след. стр. 32 ЧАСТЬ I Устаревшие сетевые API Листинг 1 -4. {продолжение) int. main(int argc, char **argv) HANDLE NCB char DWORD LANA.ENUM int if (argc != • hArray; *pncb; szSendBuff[MAX_BUFFER]; dwBufferLen, dwRet, dwlndex, dwNum; lenum; 1; 3) printf("usage: nbclient CLIENT-NAME SERVER-NAHE\n"); return 1; } // Перечисление и сброс всех номеров LANA // . . if (LanaEnum(&lenum) != NRC_GOODRET) j return 1; . t q if (ResetAll(&lenum, (UCHAR)MAX_SESSIONS, (UCHAR)MAX_NAMES, iq FALSE) != NRC_GOODRET) return 1; strcpy(szServerName, argv[2]); // // Выделение массива описателей для использования асинхронных событий. l // Выделение массива структур NCB. Нам нужен один описатель ! // и одна структура NCB для каждого номера LANA. // f hArray = (HANDLE *)GlobalAlloc(GMEM_FIXED, sizeof(HANDLE) * lenum.length); pncb = (NCB *)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(NCB) * lenum.length); // a // Создание события и присвоение его соответствующей структуре NCB, { // начало асинхронного соединения (NCBCALL). { // Не забудьте добавить имя клиента к каждому номеру // LANA, no которому он хочет соединиться. .^ for(i = 0; i < lenum.length; i++) ^ < К hArray[i] = CreateEvent(NULL, TRUE, FALSE, NULL); д pncb[i].ncb_event = hArray[i]; \ AddName(lenum.lana[i], argv[1], &dwNum); ^ Connect(&pncb[i], lenum.lana[i], szServerName, argv[1]); ^ } \д // Ожидание успеха по меньшей мере одного соединения Г Л А В А 1 Интерфейс NetBIOS 33 Листинг 1 -4. (продолжение) II dwlndex = WaitForMultipleObjectsQenum.length, hArray, FALSE, INFINITE); if (dwlndex == WAIT_FAILED) { printfC'ERROR: WaitForMultipleObjects: Xd\n", GetLastErrorO); else // Если успешно более чем одно соединение, лишние // соединения разрываются. Мы будем использовать соединение, возвращенное // WaitForMultipleObjects, иначе если оно еще не установлено, // отменим его. // for(i = 0; 1 < lenum.length; i++) { if (i != dwlndex) { if (pncb[i].ncb_cmd_cplt == NRC.PENDING) Cancel(&pncb[i]); else Hangup(pncb[i].ncb_lana_num, pncb[i].ncb_lsn); printf("Connected on LANA; Xd\n", pncb[dwlndex].ncb_lana_num); rti- // i // Отправка и прием сообщений к for(i = 0; i < 20; i i' wsprintf(szSendBuff, "Test message X03d", i); dwRet = Send(pncb[dwlndex].ncb_lana_num, "* pncb[dwlndex].ncb_lsn, szSendBuff, '*«' strlen(szSendBuff)); •A'i if (dwRet 1= NRCJ300DRET) break; dwBufferLen = MAX_BUFFER; dwRet = Recv(pncb[dwlndex].ncb_lana_num, pncb[dwlndex].ncb_lsn, szSendBuff, &dwBufferLen); if (dwRet != NRC_GOODRET) break; szSendBuff[dwBufferLen] = 0; printf("Read: • X s ' W , szSendBuff); } Hangup(pncb[dwlndex].ncb_lana_num, pncb[dwlndex].ncb_lsn); } // Очистка см. след. стр. 34 ЧАСТЬ I Устаревшие сетевые API Листинг 1-4. (продолжение) // for(i = 0, 1 < lenum length, DelName(lenum lana[i], argv[1]), CloseHandle(hArray[i]), } GlobalFree(hArray), GlobalFree(pncb), return 0, Дейтаграммные операции Дейтаграмма — это способ связи без установления логического соединения Отправитель просто направляет каждый пакет по указанному имени NetBIOS Целостность данных и порядок доставки пакетов не гарантируются Есть три способа отправить дейтаграмму Первый — направить дейтаг- рамму на определенное (уникальное или групповое) имя Это означает, что получить эту дейтаграмму может только процесс, зарегистрировавший имя приемника Второй способ — отправить дейтаграмму на групповое имя тог- да получить сообщение смогут только процессы, зарегистрировавшие дан- ное имя Наконец, третий способ — широковещательно разослать дейтаг- рамму по всей сети Такую дейтаграмму сможет получить любой процесс на любой рабочей станции в локальной сети Для отправки дейтаграммы на уникальное или групповое имя применяется команда NCBDGSEND, а для широковещания — NCBDGSENDBC Отправить дейтаграмму с помощью любой из этих команд элементарно Определите для поля ncb_num значение номера имени, возвращенного ко- мандой NCBADDNAME или NCBADDGRNAME Этот номер идентифицирует от- правителя сообщения Присвойте полю ncbjmffer значение адреса буфера, со- держащего отправляемые данные, а полю ncbjength — количество отправля- емых байтов Затем задайте для поля ncbjanajnum значение номера LANA, по которому хотите передать дейтаграмму Наконец, присвойте полю ncbjcal- Iname значение NetBIOS-имени приемника Это может быть уникальное или групповое имя Чтобы широковещательно послать дейтаграмму, выполните все описанные шаги, кроме последнего так как сообщение получат все рабо- чие станции, присвоение значения полю neb_callname не требуется Конечно, в каждом из перечисленных сценариев отправления должна быть соответствующая команда получения дейтаграммы для фактического приема данных Дейтаграммы не требуют установки соединения, если дей- таграмма достигает клиента, а у клиента нет уже ожидающей команды по- лучения, данные теряются и клиент не может их восстановить (если сервер не отправит их снова) Это недостаток дейтаграммной связи Впрочем, об- мен дейтаграммами намного быстрее, чем сеансовая связь — не нужно про- верять ошибки, устанавливать соединение и т п Г Л А В А 1 Интерфейс NetBIOS 35 Для получения дейтаграмм также предусмотрено три способа Первые два используют команду NCBDGRECV Прежде всего, вы можете отдать команду приема дейтаграммы для сообщений, направленных на конкретное имя — уникальное или групповое Во-вторых, вы вправе отдать такую команду для любой дейтаграммы, предназначенной для любого имени в таблице имен процесса NetBIOS И наконец, отдайте такую команду для широковещатель- ной дейтаграммы с помощью команды NCBDGRECVBC ПРИМЕЧАНИЕ Невозможно дать команду асинхронного приема дей- таграмм, предназначенных для имени, которое зарегистрировано иным процессом, если только оба процесса не зарегистрировали групповое имя (тогда они могут получить одно и то же сообщение) Чтобы отдать команду приема, присвойте полю ncbjium значение номе- ра имени, возвращенного успешным вызовом NCBADDNAME или NCBADDGR- NAME Этот номер определяет, для какого имени вы слушаете входящие дей- таграммы Если вы присваиваете этому полю значение OxFF, то будете полу- чать дейтаграммы, предназначенные для любого имени в таблице имен Net- BIOS этого процесса Создайте буфер для получения данных и присвойте полю ncb_buffer значение адреса буфера Присвойте полю ncbjength значение размера буфера Наконец, задайте для поля ncb_lana_num значение номера LANA, на котором надо ждать дейтаграммы После возвращения успешного вызова Netbios с командой NCBDGRECV или NCBDGRECVBC поле ncbjength будет содержать фактическое количество полученных байтов, а поле ncb_cal- Iname — NetBIOS-имя процесса отправки Код в листинге 1-5 содержит основные дейтаграммные функции Вся от- правка реализуется блокирующими вызовами как только команда выдана и данные отправлены, функция завершается и вам не грозит блокировка из- за переполнения буфера данными Вызовы приема — асинхронные события, так как неизвестно на каком из номеров LANA будут получены данные Код похож на код сервера сеансов, использующего события Для каждого номе- ра LANA код отдает асинхронную команду NCBDGRECV или NCBDGRECVBC и ждет, пока она не достигнет успеха Затем проверяются все асинхронные команды, печатаются сообщения о тех, которые были успешны, и отменяют- ся еще ожидающие команды В примере есть функции, как для направлен- ного, так и для широковещательного отправления и приема Программа мо- жет быть скомпилирована в пример приложения, конфигурируемого для от- правки или получения дейтаграмм Несколько параметров командной стро- ки позволяют пользователю указать количество отправляемых или прини- маемых дейтаграмм, задержку между отправками, использование широкове- щательных, а не направленных дейтаграмм, получение дейтаграмм для лю- бого имени и т п Листинг 1-5. Пример работы NetBIOS с дейтаграммами (Nbdgram.c) // Nbdgram с W § «include )in)t i см cjieo.cmp. 36 ЧАСТЬ I Устаревшие сетевые API Листинг 1-5. (продолжение) «include «include «include "..\Common\nbcommon.h" «define MAX.SESSIONS 254 «define MAX_NAMES 254 «define MAX_DATAGRAM_SIZE 512 BOOL bSender = FALSE, bRecvAny = FALSE, bUniqueName = TRUE, bBroadcast = FALSE, дейтаграммы? bOneLana = FALSE; char szLocalName[NCBNAMSZ + 1], szRecipientName[NCBNAMSZ + 1 ] ; DWORD dwNumDatagrams = 25, dwOneLana, dwDelay = 0; // Отправка или прием дейтаграмм // Прием для любого имени // Зарегистрировать мое имя как уникальное? // Использовать широковещательные // Использовать все LANA или только один? // Локальное NetBIOS-имя // NetBIOS-имя приема // Количество отправляемых дейтаграмм // Если использовать один LANA, то какой? // Задержка между отправками дейтаграмм // Функция: ValidateArgs // Описание: // Эта функция анализирует аргументы командной строки // и устанавливает различные глобальные флаги, соответствующие выбору void ValidateArgs(int argc, char **argv) int i; for(i = 1; i < argc; { if (strlen(argv[i]> < 2) continue; if ((argv[i][0] == •-') || (argv[i][0] { switch (tolower(argv[i][1])) V)) case 'n': // Используется уникальное имя bUniqueName = TRUE; if (strlen(argv[i]) > 2) strcpy(szLocalName, &argv[i][3]); break; case 'g 1 : // Используется групповое имя bUniqueName = FALSE; M if (strlen(argv[i]) > 2) Г Л А В А 1 Интерфейс NetBIOS 37 Листинг 1-5. if- t/ 2»' i f дв*|«грамм tf printr return strcpy(szLocalName, &argv[i][3]); break; case 's': // Отправка дейтаграмм bSender = TRUE; break; case 'с': // Количество дейтаграмм для отправки или приема if (strlen(argv[i]) > 2) dwNumDatagrams = atoi(&argv[i][3]); break; case 'r': // Имя получателя дейтаграмм if (strlen(argv[i]) > 2) strcpy(szRecipientName, &argv[i][3]); break; case 'b': // Используется широковещательная рассылка bBroadcast = TRUE; break; case 'a': // Прием дейтаграмм для любого имени bRecvAny = TRUE; break; case '1'; // Работа только на этом номере LANA bOneLana = TRUE; if (strlen(argv[i]) > 2) dwOneLana = atoi(&argv[i][3]); break; case 'd': // Задержка (в миллисекундах) между отправками if (strlen(argv[i]) > 2) dwDelay = atoi(&argv[i][3]); break; default: printf("usage: nbdgram ?\n"); break; > return; :((«• // Функция: DatagramSend // Описание: // Отправляет направленные дейтаграммы к указанному приемнику на // заданном номере LANA от данного номера имени к соответствующему приемнику. // Также указывается буфер данных и количество отправляемых байтов. int DatagramSend(int lana, int num, char «recipient, char «buffer, int buflen) см. след. стр- 38 ЧАСТЬ I Устаревшие сетевые API Листинг 1-5. (продолжение) NCB neb; ZeroMemory(&ncb, sizeof(NCB)); ncb.ncb_command = NCBDGSEND; ncb.ncb_lana_num = lana; neb.ncb_num = num; ncb.ncb_buffer = (PUCHAR)buffer; ' ncb.ncb_length = buflen; Л memset(ncb.ncb_callname, ' ', NCBNAMSZ); strncpy(ncb.ncb_callname, recipient, strlen(recipient)); ywn <•<•»«<># ч if (Netbios(&ncb) != NRC_G00DRET) { printf("Netbios: NCBDGSEND failed: Xd\n", neb.ncb_retcode); return ncb.ncb_retcode; } return NRC_G00DRET; // Функция: DatagramSendBC // // Описание: // Посылает широковещательную дейтаграму по конкретному номеру LANA с // данного номера имени. Также указаны буфер данных и количество // отправляемых байт. // int DatagramSendBC(int lana, int num, char «buffer, mt buflen) { NCB neb; ZeroMemory(&ncb, sizeof(NCB)); ncb.neb_command = NCBDGSENDBC; { neb.ncb_lana_num = lana; ?вч ncb.ncb_num = num; { ncb.ncb_buffer = (PUCHAR)buffer; 7 ncb.ncb_length = buflen; \\ >« &Q :m '* if (Netbios(&ncb) 1= NRC.GOODRET) { printfC'Netbios: NCBDGSENDBC failed: Xd\n", ncb.ncb.retcode); return ncb.ncb_retcode; } return NRC.GOODRET; J .Jflftt! 1 Г Л А В А 1 Интерфейс NetBIOS 39 Листинг 1-5. (продолжение) II Функция: DatagramRecv // // Описание: // Получает дейтаграмму на данном номере LANA направленную по имени, // представленному параметром num. Данные копируются в предоставленный буфер. // Если hEvent не 0, вызов приема выполняется асинхронно // с указанным описателем события. Если параметр num равен OxFF, слушает // дейтаграммы, предназначенные для любого имени NetBIOS, // зарегистрированного процессом. int DatagramRecv(PNCB pncb, int lana, int num, char «buffer, int buflen, HANDLE hEvent) { ZeroMemory(pncb, sizeof(NCB)); if (hEvent) { pncb->ncb_command = NCBDGRECV | ASYNCH; pncb->ncb_event = hEvent; } else pncb->ncb_command = NCBDGRECV; pncb->ncb_lana_num = lana; pncb->ncb_num = num; pncb->ncb_buffer = (PUCHAR) buffer;,^) pncb->ncb_length = buflen; „ s , a j A i ! ч1 if (Netbios(pncb) != NRC_G00DRET) { printf("Netbos: NCBDGRECV failed: И\п", pncb->ncb_retcode); return pncb->ncb_retcode; } I8J*. return NRC_GOODRET; ,| // Функция: DatagramRecvBC // // Описание: // Получает широковещательную дейтаграмму на данном номере LANA. // Данные копируются в предоставленный буфер. Если hEvent не равно О, // вызов приема выполняется асинхронно с указанным // описателем события. // int DatagramRecvBC(PNCB pncb, int lana, int num, char «buffer, int buflen, HANDLE hEvent) ZeroHemory(pncb, sizeof(NCB)); if (hEvent) ,"n/bt :to9ix*1 см.след.стр. Листинг 1-5. (продолжение) { pncb->ncb_command = NCBDGRECVBC | ASYNCH; pncb->ncb_event = hEvent; } else pnob->ncb_comrnand = NCBDGRECVBC; pncb->ncb_lana_num = lana; *t t pncb->ncb_num = nuin; Ь > pncb->ncb_buffer = (PUCHAR)buffer; ы к ч pncb->ncb_length = buflen; tni 'if (Netbios(pncb) != NRC_G00DRET) *{ printf("Netbios: NCBDGRECVBC failed: Xd\n", pncb->ncb_retcode); return pncb->ncb_retcode; return NRC_G00DRET; щ II Функция: main // % II Описание: З // Инициализирует интерфейс NetBIOS, выделяет ресурсы, а затем отправляет илщ // получает дейтаграммы согласно пользовательским параметрам I // int main(int argc, char *»argv) W; 4 i ) LANA_ENUM lenum; »«")t-- , int i, j ; •*} '*' char ' '' szMes9age[MAX_DATAGRAM_SIZE], { szSender[NCBNAMSZ + 1 ] ; < ЗЙТО8.,/ '«Jei DWORD «dwNufli = NULL, { dwBytesRead, |