Главная страница

Программирование в сетях Windows. Э. Джонс, Д. Оланд


Скачать 2.88 Mb.
НазваниеЭ. Джонс, Д. Оланд
АнкорПрограммирование в сетях Windows.pdf
Дата12.10.2017
Размер2.88 Mb.
Формат файлаpdf
Имя файлаПрограммирование в сетях Windows.pdf
ТипКнига
#9346
страница4 из 50
1   2   3   4   5   6   7   8   9   ...   50

NCBNAMSZ);
см. след. стр.

Листинг 1 -2. (продолжение')
pncb->ncb_callname[O] = '•';
_/
if (Netbios(pncb) != NRC.GOODRET)
{
printfC'ERROR: Netbios: NCBLISTEN: Xd\n", pncb->ncb_retcode);
return pncb->ncb_retcode;
}
return NRC_GOODPET;
}

и
II Функция: main ,"n/bl :ЧШ>'
//
// Описание:
// Инициализирует интерфейс NetBIOS, выделяет некоторые ресурсы, добавляет
// имя сервера к каждому номеру LANA и дает асинхронную команду NCBLISTEN на
// каждый номер LANA с соответствующим обратным вызовом. Затем ждет входящих
// клиентских соединений, порождая рабочий поток, чтобы
// работать с ними. Главный поток просто ждет, пока серверные
// потоки работают с клиентскими запросами. Вы навряд ли будете делать это в {
II настоящем приложении: этот пример приведен только для иллюстрации.
// \\
i n t main(int argc, char **argv) , <д
< w
LANA.ENUM lenum; д int i, ,\
num; д
A
// Перечисляются и инициализируются все номера LANA д
// I
1
*ni if (LanaEnum(&lenum) != NRC_GOODRET) }
return 1; n
if (ResetAlK&lenum, 254, 254, FALSE) != NRC.GOODRET)
return 1;
//
// Каждому номеру LANA добавляется имя сервера и дается команда слушать
//
for(i = 0; i < lenum.length;
AddName(lenum.lana[i], SERVER_NAME, inura);
Listen(lenum.lana[i], SERVER_NAME);
> ,i
\
while (1) \
< \
Sleep(5000); \
> S

Г Л А В А 1 Интерфейс NetBIOS 23
Далее функция main добавляет имя вашего процесса к каждому номеру
LANA, на котором вы хотите принимать соединения. Сервер добавляет имя своего процесса — TEST-SERVER-1, к каждому номеру LANA. Это имя (допол- ненное пробелами, конечно) клиенты будут использовать для соединения с сервером. При попытке установить или принять соединение важен каждый символ в имени NetBIOS. Большинство проблем, с которыми сталкиваются при программировании клиентов и серверов NetBIOS, связаны с несоответ- ствием имен. Будьте последовательны, дополняя имена пробелами или ка- ким-либо другим символом. Пробелы — наиболее популярные символы-за- полнители, так как их удобно перечислять и распечатывать.
Последний и наиболее важный шаг для сервера — асинхронно выдать ряд команд NCBLISTEN. Функция Listen сначала выделяет структуру NCB. При ис- пользовании асинхронных вызовов NetBIOS, переданная вами структура
NCB должна сохраняться с момента вызова до его завершения. Для этого тре- буется динамически выделять каждую структуру NCB перед выдачей коман- ды или поддерживать глобальный пул структур NCB, чтобы использовать его в асинхронных запросах. Для команды NCBLISTEN задайте номер LANA, к ко- торому должен обращаться вызов. Заметьте, что код в листинге 1-1 выпол- няет логическую операцию OR над командами NCBLISTEN и ASYNCH. При указании команды ASYNCH поле ncb_post или ncb_event не должно равнять- ся 0, иначе вызов Netbios даст ошибку NRCJLLCMD.
В листинге 1-2 функция Listen присваивает полю ncb_post функцию обрат- ного вызова ListenCallback. Затем функция Listen присваивает полю ncbname
имя процесса сервера. С этим именем будут соединяться клиенты. Функция также записывает в первый символ поля ncb_callname астериск (*). Это оз- начает, что сервер примет соединение от любого клиента. С другой сторо- ны, вы можете поместить в поле ncbcallname конкретное имя — тогда толь- ко зарегистрированный под этим именем клиент сможет соединиться с сер- вером. Наконец, функция Listen обращается к Netbios. Запрос завершается немедленно, и функция Netbios до завершения команды присваивает полю
ncbjzmdjzplt переданной структуры NCB значение NRC PENDING (OxFF).
После того как main инициализируется и даст команду NCBLISTEN каж- дому номеру LANA, поток main переходит в бесконечный цикл.
ПРИМЕЧАНИЕ Так как этот сервер — только пример, схема его рабо- ты очень проста. При разработке собственных серверов NetBIOS вы можете написать другую обработку в главном цикле или выдать в цик- ле main синхронную команду NCBLISTEN одному из LANA.
Функция обратного вызова выполняется, только когда входящее соедине- ние принято на номере LANA. Когда команда NCBLISTEN принимает соеди- нение, она вызывает эту функцию в поле ncb_post с исходной структурой
NCB в качестве параметра. Полю ncb_retcode присваивается значение кода возврата. Всегда проверяйте это значение, чтобы увидеть, последовало ли клиентское соединение. В случае успешного соединения поле ncb_retcode
будет равно NRCjSOODRET (0x00).

Если соединение успешно, асинхронно выдайте другую команду NCBLISTEN
тому же самому номеру LANA. Это необходимо потому, что как только исход- ное прослушивание завершится успехом, сервер прекратит слушать клиентс- кие соединения на этом LANA, пока не будет дана другая команда NCBLISTEN.
Таким образом, чтобы упростить доступ к вашим серверам, вызовите не- сколько команд NCBLISTEN для одного LANA — тогда будет можно одновре- менно принимать соединения от множества клиентов. Наконец, функция об- ратного вызова создает поток, который будет обслуживать клиента. В нашем примере данный поток просто работает в цикле и вызывает команду блоки- рующего чтения (NCBRECV), а затем — блокирующей передачи (NCBSEND).
В примере реализован эхо-сервер, читающий сообщения клиентов и отправ- ляющий их обратно. Клиентский поток входит в цикл, пока клиент не пре- рвет соединение, после чего клиентский поток дает команду NCBHANGUP,
чтобы закрыть соединение со своей стороны. С этого момента клиентский поток освобождает структуру NCB и завершается.
Для сеансов данные буферизируются нижележащими протоколами, так что наличие невыполненных запросов на прием необязательно. После того,
как дана команда приема, функция Netbios немедленно передает доступные данные в предоставленный буфер и вызов возвращается. Если доступных данных нет, вызов приема блокируется, пока не появятся доступные данные или не прервется сеанс. То же верно для команды отправления: если сетевой стек способен отправить данные немедленно по проводу или буферизовать их в стеке для дальнейшей передачи, вызов возвращается немедленно. Если у системы нет буферного пространства, чтобы немедленно отправить дан- ные, запрос на отправку блокируется, пока буфер не освободится.
Для решения этой проблемы можно задействовать команду ASYNCH при передаче и приеме. Буфер, предоставленный для асинхронной передачи и приема, должен быть виден за пределами вызывающей процедуры. Еще один способ обойти блокирование заключается в использовании полей ncb_sto и
ncbjrto. Поле ncb_sto задает тайм-аут отправления. Указывая ненулевое зна- чение в 500-миллисекундных единицах, вы можете задать максимальную продолжительность блокирования отправки перед возвратом. Если время команды истекает, данные не посылаются. То же верно для времени ожида- ния приема: если данные не получены за указанное время, вызов заверша- ется без передачи данных в буфер.
Сервер сеансов: модель асинхронных событий
В листинге 1-3 приведен код эхо-сервера, отличный от кода в листинге 1-2.
Здесь в качестве механизма оповещения о завершении используются собы- тия Win32. Модель событий похожа на модель обратного вызова, единствен- ное отличие: в модели обратного вызова система выполняет ваш код, когда асинхронная операция завершается, а в модели событий — приложение дол- жно убедиться в завершении операции, проверяя состояние события. По- скольку речь идет о стандартных событиях Win32, вы можете использовать любую из доступных процедур синхронизации, например WaitForSingleEvent
или WaitForMultipleEvents. Модель событий более эффективна, так как застав-

1 интерфейс NetBIOS 25
ляет программиста структурировать программу, чтобы сознательно прове- рять завершение.
Наш сервер модели событий начинает работу с того же, что и сервер об- ратного вызова.
1. Перечисляет номера LANA.
2. Сбрасывает все номера LANA.
3. Добавляет имя сервера к каждому номеру LANA.
4. Инициирует прослушивание на каждом LANA.
Единственное различие — нужно отслеживать все невыполненные коман- ды прослушивания, чтобы непременно сопоставить завершение события с соответствующими блоками NCB, инициализирующими конкретную коман- ду. Код в листинге 1-3 выделяет массив структур NCB, размер которого со- ответствует количеству номеров LANA (поскольку требуется выполнять ко- манду NCBLISTEN для каждого номера). Дополнительно код создает событие для каждой структуры NCB, чтобы оповещать о завершении команды. Функ- ция Listen использует одну из структур NCB из массива в качестве параметра.
Листинг 1-3. Сервер на основе асинхронных событий (Evnbsvr.c)
// Evnbsvr.c
«include
«include
«include
«include ".. \Common\nbcommon.h"
«define MAX_SESSIONS 254
«define MAX_NAMES 254
«define MAX_BUFFER 2048
«define SERVER_NAME "TEST-SERVER-1"
NCB *g_Clients=NULL; // Global NCB structure for clients
// Функция: ClientThread
//
// Описание:
// Этот поток берет структуру NCB из сеанса соединения
// и ждет входящих данных, которые затем посылает обратно
// клиентам, пока сеанс не будет закрыт.
//
DWORD W1NAPI ClientThread(PVOID lpParam)
{
PNCB pncb = (PNCB)lpParam;
NCB neb;
char szRecvBuff[MAX_BUFFER],
см. след. стр.

Листинг 1-3. (продолжение)
szClientName[NCBNAHSZ + 1];
DWORD dwBufferLen = MAX_BUFFER, /
dwRetVal = NRC_GOODRET;
// Отправка и прием сообщений, пока сеанс не будет закрыт
//
FormatNetbiosName(pncb->ncb_callname, szClientName);
while (1)
{
dwBufferLen = MAX_BUFFER;
dwRetVal = Recv(pncb->ncb_lana_num, pncb->ncb_lsn,
szRecvBuff, idwBufferLen);
if (dwRetVal != NRC_GOODRET)
break;
szRecvBuff[dwBufferLen] = 0;
printf("READ [LANA=Xd]:
-
Xs'\n", pncb->ncb_lana_num,
szRecvBuff);
dwRetVal = Send(pncb->ncb_lana_num, pncb->ncb_lsn,
szRecvBuff, dwBufferLen);
if (dwRetVal != NRC.GOODRET)
break;
}
prmtf("Client 'Xs' on LANA Xd disconnected\n", szClientName,
pncb->ncb_lana_num); щ
//
// Если в ходе чтения или записи выдается ошибка NRC.SCLOSED, ^
// то все в порядке; иначе возникла некоторая другая ошибка, так что щ
II соединение разрывается с этой стороны. ,
// л
if (dwRetVal != NRC SCLOSED) i»
{ '
ZeroMemory(&ncb, sizeof(NCB)); §
ncb.ncb_command = NCBHANGUP;
'*
ft ncb.ncb_lsn = pncb->ncb_lsn; >"
x%
ncb.ncb_lana_num = pncb->ncb_lana_num; 3 ^
\\
if (Netbios(&ncb) != NRC_GOODRET) ^
{
\
printf("ERROR: Netbios: NCBHANGUP: Xd\n",
v neb.ncb_retcode);
GlobalFree(pncb); v dwRetVal = neb.ncb_retcode; ,f
// Передаваемая структура NCB выделяется динамически, поэтому ь
// ее следует вначале удалить /

Листинг 1-3. (продолжение)
GlobalFree(pncb);
return NRC_G00DRET;
// Функция: Listen
//
// Описание:
// Дается команда асинхронно слушать на указанном LANA.
// В переданной структуре NCB полю ncb_event уже
// присвоено значение действительного описателя события Windows.
//
int Listen(PNCB pncb, int lana, char «name)
{
pncb->ncb_command = NCBLISTEN | ASYNCH;
pncb->ncb_lana_num = lana;
//
// Это имя, с которым будут соединяться клиенты
//
memset(pncb->ncb_name, ' ', NCBNAMSZ);
strncpy(pncb->ncb_name, name, strlen(name));
//
// '*' означает, что мы примем клиентское соединение от любого клиента.
// Задавая здесь конкретное имя, мы разрешаем соединение только с
// указанным клиентом.
//
memset(pncb->ncb_callname, ' ', NCBNAMSZ);
pncb->ncb_callname[O] = '*';
if (Netbios(pncb) != NRC_GOODRET)
{
printf("ERROR: Netbios: NCBLISTEN: Xd\n", pncb->ncb_retcode);
return pncb->ncb_retcode;
}
return NRC GOODRET;
// Функция: main
//
// Описание:
// Инициализирует интерфейс NetBIOS, выделяет некоторые ресурсы, и
// дает асинхронную команду слушать каждому LANA, используя события. Ждет
// пока событие не сработает, а затем обрабатывает клиентское соединение.
//
int main(int argc, char **argv)
{
PNCB pncb=NULL;
см. след. стр.

Листинг 1 -3. (продолжение)
HANDLE hArray[64],
hThread; ^y
DWORD dwHandleCount=0,
dwRet,
dwThreadld;
int i,
num;
LANA_ENUM lenum;
// Перечисление и сброс всех номеров LANA
//
if (LanaEnum(&lenum) != NRC_GOODRET)
return 1;
if (ResetAlK&lenum, (UCHAR)MAX_SESSIONS, (UCHAR)MAX_NAMES,
FALSE) != NRC_GOODRET)
return 1;
//
// Выделение массива структур NCB (по одной для каждого номера LANA)
//
g_Clients = (PNCB)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
sizeof(NCB) * lenum.length);
//
// Создание событий, добавление имени сервера каждому номеру LANA и начало
// асинхронного прослушивания на каждом LANA.
//
for(i = 0; i < lenum.length; i++)
{
hArray[i] = g_Clients[i].ncb_event = CreateEvent(NULL, TRUE, щ
FALSE, NULL);
4
AddName(lenum.lana[i], SERVER_NAME, &num); \
Listen(&g_Clients[i], lenum.lana[i], SERVER.NAME);
}
while (1) {
{ IUJ
1
»'
// Ожидание подключения клиента {
//
dwRet = WaitForMultipleObjectsdenum.length, hArray, FALSE, \V
INFINITE); I - '• \\
if (dwRet == WAIT_FAILED) \
{ •*> \
printf("ERROR: WaitForMultipleObjects: Xd\n",
GetLastErrorO); \
break; \
}
// Проверка всех структур NCB для определения, достигла ли успеха более г
// чем одна структура. Если поле ncb_cmd_plt не содержит значение }
// NRC_PENDING, значит существует клиент, необходимо создать поток и
(
// выделить ему новую структуру NCB.

Листинг 1 -3. (продолжение)
II Нам нужно многократно использовать исходную структуру
// NCB для других клиентских соединений.
//
for(i = 0; i < lenum.length; i++)
<
if (g_Clients[i].ncb_cmd_cplt != NRC_PENDING)
{
pncb = (PNCB)GlobalAlloc(GMEM_FIXED, sizeof(NCB));
memcpy(pncb, &g_Clients[i], sizeof(NCB));
pncb->ncb_event = 0;
hThread = CreateThread(NULL, 0, ClientThread,
(LPVOID)pncb, 0, &dwThreadId);
CloseHandle(hThread);
//
// Описатель сбрасывается, асинхронно начинается еще одно
// прослушивание ResetEvent(hArray[i]);
//
Listen(&g Clients[i], lenum.lana[i], SERVER_NAME);
// Очистка
о //
for(i = 0; i < lenum.length; i
? <

* DelName(lenum.lana[i], SERVER_NAHE);
nil
CloseHandle(hArray[i]);
к.
}
is
GlobalFree(g_Clients);
- return 0;
Первый цикл функции main получает доступные номера LANA и при этом добавляет имя сервера и отдает команду NCBLJSTEN для каждого LANA, а так- же формирует массив описателей событий. Затем вызывается функция Wait-
ForMultipleObjects, которая блокируется, пока по меньшей мере один из опи- сателей не перейдет в занятое состояние. Тогда WaitForMultipleObjects завер- шается, и код порождает поток для чтения входящих сообщений и отправ- ки их обратно клиенту. Код копирует занятую структуру NCB, чтобы передать ее в клиентский поток. Это позволяет многократно использовать исходную структуру NCB для асинхронного вызова других команд NCBLISTEN, что мож- но сделать путем сброса события и повторного вызова функции Listen для этой структуры. Не обязательно копировать всю структуру — на самом деле вам нужны только локальный номер сеанса {ncbjsri) и номер LANA
(ncbjanajnctri). Впрочем, структура NCB — удобный контейнер для хране- ния обоих значений, чтобы потом передать их в один параметр потока. Кли-
ентский поток, используемый моделью событий, тот же, что и для модели обратног о вызова, за исключением оператора GlobalFree
Асинхронные стратегии сервера
При использовании обоих видов серверов существует вероятность что кли- енту будет отказано в обслуживании По завершении команды NCBLJSTEN
происходит небольшая задержка, которая длится вплоть до вызова функции обратного вызова или пока событие не будет занято Рассмотренные нами серверы не отдают асинхронно еще одну команду NCBLJSTEN ранее, чем че- рез несколько операторов Например, если сервер обслуживал клиента на номере LANA 2 и еще один клиент попытается подключиться прежде, чем сервер отдаст следующую команду NCBLJSTEN на том же номере LANA, кли- ент получит ошибку NRC_NOCALL (0x14) Это означает, что для данного име- ни еще не была асинхронно вызвана команда NCBLJSTEN Чтобы избежать та- кой ситуации, сервер может асинхронно отдавать несколько команд NCBLJSTEN
на каждом LANA
Как видите, использовать асинхронные команды достаточно просто Флаг
ASYNCH может применяться почти к любой команде NetBIOS Помните толь- ко, что структура NCB, которую вы передаете Netbios, должна быть видима глобально
Клиент сеанса NetBIOS [
Клиент NetBIOS напоминает асинхронный сервер событий Листинг 1 -4 со- держит пример кода для клиента Клиент выполняет уже знакомые нам стан- дартные шаги инициализации имени добавляет свое имя к таблице имен каж- дого номера LANA и затем дает асинхронную команду соединения Цикл main ждет, пока одно из событий не будет занято В это время в цикле проверяется поле ncb_cmdj3ptt каждой структуры NCB, соответствующей отданным коман- дам подключения, по одной для каждого номера LANA Если поле ncb_cmd_cplt
равно NRC_PENDLNG — код отменяет асинхронную команду, если команда за- вершается (соединение установлено) и данная NCB не соответствует опове- щенной (определяется по значению, возвращаемому WaitForMultipleObjects) —
соединение разрывается Когда сервер слушает на каждом номере LANA на своей стороне и клиент пытается соединиться на каждом из его номеров
LANA, может быть установлено несколько соединений В этом случае код про- сто закрывает лишние соединения командой NCBHANGUP — нужна связь толь- ко по одному каналу Попытки соединения с каждым номером LANA с обеих сторон практически гарантированно успешны ,
1   2   3   4   5   6   7   8   9   ...   50


написать администратору сайта