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

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


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

1 2 ЧАСТЬ I Устаревшие сетевые API
Листинг 1 - 1 . (продолжение)
«include 'nbcommon h"
//
// Перечисляются все номера LANA
//
int LanaEnum(LANA_ENUM «lenum)
{
NCB neb,
ZeroMemory(&ncb, sizeof(NCB));
neb ncb_command = NCBENUH;
neb ncb_buffer = (PUCHAR)lenum;
neb ncb_length = sizeof(LANA_ENUH);
if (Netbios(&ncb) i= NRC_GOODRET)
{
prmtfC'ERROR- Netbios: NCBENUM: Xd\n", neb. ncb_retcode);
return ncb.ncb_retcode;
>
return NRC_G00DRET;
// Сброс всех сведений о LANA, перечисленных в структуре LANA_ENUM,
// а также настройка среды NetBIOS (максимальное количество сеансов,
// максимальный размер таблицы имен),и использование первого NetBIOS-имени.
//
int ResetAll(LANA_ENUM *lenum, UCHAR ucMaxSession,
UCHAR ucMaxName, BOOL bFirstName)
{
NCB neb;
int i;
ZeroMemory(&ncb, sizeof(NCB));
nob.ncb_command = NCBRESET;
ncb.ncb_callname[O] = ucMaxSession;
ncb.ncb_callname[2] = ucMaxName;
ncb.ncb_callname[3] = (UCHAR)bFlrstName;
f o r ( i = 0; l < lenum->length,
neb ncb_lana_num = lenum->lana[i];
if (Netbios(&ncb) 1= NRC_GOODRET)
{
printf("ERROR Netbios NCBRESET[Xd]: Xd\n"
neb ncb_lana_num, ncb.ncb_retcode);
return ncb.ncb_retcode;

Г Л А В А 1 Интерфейс NetBIOS
13
Листинг 1-1. (продолжение)
return NRC_G00DRET,
// Добавление указанного имени данному LANА. Возвращает номер
// для зарегистрированного имени
//
int AddName(mt lana, char «name, int *num)
{
NCB neb,
ZeroMemory(&ncb, sizeof(NCB));
neb ncb_command = NCBADDNAME,
neb ncb_lana_num = lana;
memset(ncb.ncb_name, ' ', NCBNAMSZ);
strncpy(ncb.ncb_name, name, strlen(name));
if (Netbios(&ncb) i= NRC_GOODRET)
{
pnntf ("ERROR Netbios NCBADDNAME[lana=Xd;name=Xs]: Xd\n",
lana, name, neb ncb_retcode);
return neb ncb_retcode;
>
" *num = neb ncb_num;
return NRC_G0ODRET,
// Добавление указанного группового имени NetBIOS данному LANA
// Возвращение номера добавленного имени.
//
int AddGroupName(int lana, char «name, int *num)
{
NCB neb;
ZeroMemory(&ncb, sizeof(NCB));
neb ncb_command = NCBADDGRNAME;
ncb.ncb_lana_num = lana,
memset(ncb.ncb_name, ' ', NCBNAMSZ);
strncpy(ncb.nob_name, name, strlen(name));
if (Netbios(&ncb) i= NRC_G00DRET)
{
printfC ERROR Netbios NCBADDGRNAME[lana=Xd;name=Xs]: Xd\n",
lana, name, neb ncb_retcode);
return neb ncb_retcode;
}
*num = neb.ncb_num,
см. след. стр.

1 4 ЧАСТЬ I Устаревшие сетевые API
Листинг 1-1. (продолжение)
return NRCJ300DRET;
// Удаление данного имени NetBIOS из таблицы имен,
// связанной с номером LANA
//
int DelName(int lana, char *name)
{
NCB neb;
ZeroMemory(&ncb, sizeof(NCB));
neb ncb_command = NCBDELNAME,
neb ncb_lana_num = lana;
memset(ncb.ncb_name, ' , NCBNAMSZ),
strncpy(ncb.ncb_name, name, strlen(name));
if (Netbios(Sncb) ' = NRC_GOODRET)
{
pnntf ("ERROR: Netbios NCBADDNAME[lana=!(d; name=Xs]: Xd\n"
lana, name, neb.ncb_retcode);
return neb.ncb_retcode;
}
return NRC_GOODRET;
// Отправка len байт из буфера данных по указанному сеансу (lsn) \ ,
// и номеру lana , s
// . ^
int Send(int lana, int lsn, char «data, DWORD len) Д
{ - tnt
NCB neb, }
int retcode; y^i*
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBSEND,
ncb.ncb_buffer = (PUCHAR)data,
ncb.ncb_length = len;
ncb.ncb_lana_num = lana;
ncb.ncb_lsn = lsn,
«I
retcode = Netbios(&ncb);
It return retcode; ,i
// Прием не более len байт в буфер данных по указанному сеансу

Г Л А В А 1 Интерфейс
Листинг 1 - 1 . (продолжение)
Ц (lsn) и номеру lana
//
int Recv(int lana, mt lsn, char «buffer, DWORD *len)
{
NCB neb;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBRECV;
ncb.ncb_buffer = (PUCHAR)buffer;
ncb.ncb_length = *len;
ncb.ncb_lana_num = lana;
ncb.ncb_lsn = lsn;
if (Netbios(&ncb) != NRC.GOODRET)
{
•len = -1;
return ncb.ncb_retcode;
>
*len = ncb.ncb_length;
return NRC_GOODRET;
}
//
// Прекращение указанного сеанса на данном номере lana
//
int Hangup(int lana, int lsn)
NCB
int
ZeroMemory(&ncb,
neb.
neb.
neb.
neb.
neb.
neb
.command =
.lsn = lsn lana num neb;
retcode;
sizeof(NCB));
NCBHANGUP;
= lana;
retcode = Netbios(&nob);
return retoode;
}
//
// Отмена данной асинхронной команды, указанной в параметре-структуре NCB
//
int Cancel(PNCB pneb)
{
NCB neb;
ZeroMemory(&ncb, sizeof(NCB));
см. след. стр.

1 6 Ч А С Т Ь I Устаревшие сетевые API
Листинг 1 - 1 . (продолжение)
ncb.ncb_command = NCBCANCEL;
ncb.ncb_buffer = (PUCHAR)pncb;
neb.ncb_lana_num = pncb->ncb_lana_num;
if (Netbios(&ncb) != NRC_GOODRET)
<
printf("ERROR: NetBIOS: NCBCANCEL: Xd\n", ncb.nob_retcode);
return ncb.ncb_retcode;
} ••'
return NRC.GOODRET;
// Форматирование указанного имени NetBIOS, чтобы оно было пригодно для печати.
// Непечатаемые символы заменяются точками.
// Буфер outname содержит возвращенную строку, длина которой - минимум
// NCBNAMSZ + 1 символов.
//
int FormatNetbiosName(char «nbname, char «outname)
{
int i; 'Jt9i
i
strncpy(outname, nbname, NCBNAMSZ); V
outname[NCBNAMSZ - 1] = Д О
1
; *» f > ' >«|. \ \\
for(i = 0; i < NCBNAMSZ - 1; i++) \\
{ Jnr
// Если символ непечатаемый, он заменяется точкой. )
II
if (!((outname[i] >= 32) && (outname[i] <= 126)))
outname[i] = '.';
}
return NRC_GOODRET;
>
Первая из типичных процедур, приведенных в файле Nbcommon.c —
LanaEnum, самая распространенная: ее используют почти все приложения
NetBIOS. Эта функция перечисляет доступные номера LANA в данной систе- ..
ме. Функция обнуляет структуру NCB, присваивает полю nebjoommand зна- чение NCBENUM, полю ncb_buffer— структуру LANA_ENUM и полю nebjength
значение размера структуры LANAJ5NUM. При правильной инициализации структуры NCB единственное действие, которое должна предпринять функ- ция LanaEnum, чтобы вызвать команду NCBENUM — вызвать функцию Netbios.
Как видите, выполнить команду NetBIOS несложно. В случае синхронных ко- манд возвращаемое Netbios значение сообщит, успешно ли выполнена эта команда. Константа NRCjGOODRETвсегда соответствует успеху.
В случае успешного вызова NetBIOS в предоставленную структуру LA-
NAJZNUM будут записаны количество номеров LANA на текущем компьюте- ре, а также фактические номера LANA. Структура LANA_ENUM определена следующим образом:

Г Л А В А 1 Интерфейс NetBIOS 17
typedef struct LANA_ENUM .
{
UCHAR length;
UCHAR lana[MAX_LANA + 1 ] ;
} LANA_ENUM, *PLANA_ENUM;
Поле length указывает, сколько номеров LANA имеет локальный компьютер,
поле lana — массив фактических номеров LANA, значение length — сколько элементов массива lana будет заполнено номерами LANA.
Следующая функция — ResetAll, также используется во всех приложениях
NetBIOS. Хорошо написанная программа NetBIOS должна обнулить каждый номер LANA, который планирует применить. Как только вы получаете струк- туру LANA_ENUM с номерами LANA от LanaEnum, можете сбросить их, вызвав команду NCBRESET для каждого номера LANA в структуре. Это именно то, что делает ResetAll; первый параметр функции — структура LANA_ENUM. Для сбро- са требуется, только чтобы функция присвоила полю neb command значение
NCBRESET, а полю ncbjanajium — номер LANA, который требуется сбросить.
Хотя некоторые платформы, типа Windows 95, не требуют инициализировать каждый используемый номер LANA, лучше все-таки это делать. Windows NT
требует инициализировать каждый номер LANA до его использования, иначе любые другие запросы KNetbios вернут ошибку 52 (NRCjENVNOTDEF).
Кроме того, при сбросе номера LANA вы можете задать определенные параметры среды NetBIOS через символьные поля ncbjzallname. Другие па- раметры ResetAll соответствуют этим параметрам окружения. Функция ис- пользует параметр ucMaxSession, чтобы присвоить значение нулевому сим- волу ncb_callname, который задает максимальное количество сеансов. Обыч- но операционная система задает значение по умолчанию меньше максиму- ма. Например, в Windows NT 4 по умолчанию допустимо 64 параллельных сеанса. ResetAll присваивает символу 2 поля ncb_callname (определяющему максимальное число имен NetBIOS, которые могут быть добавлены к каждо- му номеру LANA) значение параметра ucMaxName. Опять же, ОС определя- ет стандартный максимум. Наконец, ResetAll присваивает символу 3, исполь- зуемому для клиентов NetBIOS, значение своего параметра bFirstName. При- сваивая этому параметру TRUE, клиент использует имя компьютера как имя его процесса NetBIOS. В результате клиент может соединяться с сервером и отправлять данные, не принимая никаких входящих соединений. Этот пара- метр ускоряет инициализацию (добавление имени NetBIOS в локальную таб- лицу требует времени).
Операцию добавления имени в локальную таблицу имен выполняет функ- ция AddName. Параметры: добавляемое имя и номер LANA, к которому оно добавляется. Помните, что таблица имен у каждого LANA своя, и если вы хо- тите, чтобы ваше приложение обслуживало соединения на любом доступном номере LANA, добавьте имя процесса к каждому LANA. Команда для добавле- ния уникального имени — NCBADDNAME. Другие обязательные поля — номер
LANA, для которого добавляется имя, и добавляемое имя, которое должно быть скопировано в поле ncbjiame. AddName сначала заполняет буфер ncbjiame
пробелами и предполагает, что параметр пате указывает на строку с симво-
лом /О в конце. После успешного добавления имени Netbios возвращает в поле
ncbjium номер добавленного имени NetBIOS. Это значение используется для дейтаграмм (подробно мы обсудим их далее), чтобы идентифицировать ис- ходный процесс NetBIOS. Наиболее типичная ошибка, с которой сталкивают- ся при добавлении уникального имени — NRCJDUPNAME, происходит, когда имя уже используется другим процессом в сети.
AddGroupName работает так же, как AddName, за одним исключением:
AddGroupName запускает команду NCBADDGRNAME и никогда не вызывает ошибку NRCJDUPNAME.
Функция DelName удаляет имя NetBIOS из таблицы имен. Ей требуются только номер LANA, для которого вы хотите удалить имя, и само имя.
Следующие две функции в листинге 1-1 — Send и Recv, используются для отправки и получения данных в сеансе связи. Эти функции почти идентич- ны за исключением значения поля neb_command. ему присваивается NCBSEND,
либо NCBRECV. Обязательные параметры команды: номер LANA, по которому будут отправлены данные, и номер сеанса. Успешная команда NCBCALL или
NCBLISTEN возвращает номер сеанса. Клиенты используют команду NCBCALL
для соединения с известной службой, а серверы — команду NCBLISTEN, что- бы ждать входящих клиентских соединений. Когда какая-либо из этих команд успешно выполняется, интерфейс NetBIOS устанавливает сеанс с уникальным целым идентификатором.
Send и Recv также требуют параметров, которые проецируются в поля
ncbjouffer (буфера) и ncbjength (длины). При отправке данных поле ncb_buffer
указывает на содержащий эти данные буфер. Поле длины задает количество отправляемых символов в буфере. При получении данных поле буфера ука- зывает на блок памяти, в который копируются входящие данные. Поле дли- ны содержит размер блока памяти. Когда функция Netbios завершает рабо- ту, она записывает в поле длины количество успешно полученных байтов.
Важный аспект отправки данных через сеанс: вызов функции Send не осуществим, пока получатель не вызовет функцию Recv. To есть если отпра- витель выдает большое количество данных, а получатель не читает их, ис- пользуются значительные ресурсы для локальной буферизации данных.
Поэтому лучше вызывать немного команд NCBSEND или NCBCHAINSEND од- новременно. Для решения этой проблемы используйте Netbios-команды
NCBSENDNA и NCBCHAINSENDNA. В этом случае отправлять данные можно без подтверждения получателя.
Последние две функции в конце листинга 1-1 — Hangup и Cancel, предназ- начены для закрытия установленных сеансов или отмены невыполненной команды. Вызов команды NetBIOS NCBHANGUP позволит корректно завершить сеанс. При этом все невыполненные запросы на получение данных заверша- ются и возвращают ошибку закрытого сеанса — NRC_SCLOSED (0x0А). Если какие-либо команды отправки данных не выполнены, команда завершения связи блокируется вплоть до их выполнения. Эта задержка происходит не- зависимо от того, передает ли команда данные или ожидает, когда удален- ная сторона запросит прием данных.
интерфейс iNeitsiUb 1У
Сервер сеансов: модель асинхронного обратного вызова
Займемся сервером, который будет слушать входящие клиентские соедине- ния. Это простой эхо-сервер, отправляющий обратно любые данные, полу- ченные от клиента. Листинг 1-2 содержит код сервера, использующего асин- хронные функции обратного вызова. Код взят из файла Cbnbsvr.c, на прила- гаемом компакт-диске он находится в папке /Examples/ChapterOl /Server. В
функции main сначала перечисляются доступные номера LANA с помощью
LanaEnum, а затем — сбрасывается каждый номер LANA с помощью ResetAll.
Помните, что эти два шага обычно требуются от всех приложений NetBIOS.
Листинг 1-2. Сервер асинхронного обратного вызова (Cbnbsvr.c)
// Cbnbsvr.c
«include
«include
«include
«include "..\Common\nbcommon.h"
«define MAX.BUFFER 2048
«define SERVER_NAME "TEST-SERVER-1"
DWORD WINAPI ClientThread(PVOID lpParam);
// Функция: ListenCallback
//
// Описание:
// Эта функция вызывается, когда завершается асинхронное прослушивание.
// Если не происходит никакой ошибки, создает поток, чтобы работать клиентом.
// Также асинхронно дается команда слушать другие клиентские соединения.
//
void CALLBACK ListenCallback(PNCB pncb)
{
HANDLE hThread;
DWORD dwThreadld;
if (pncb->ncb_retcode != NRC_GOODRET)
{
printf("ERROR: ListenCallback: Xd\n", pncb->ncb_retcode);
return;
}
Listen(pncb->ncb_lana_num, SERVER_NAME);
hThread = CreateThread(NULL, 0, ClientThread, (PVOID)pncb, 0,
&dwThreadId);
if (hThread == NULL)
ы AMAJ no
,(«ш< do. см. след. стр.

20 ЧАСТЬ I Устаревшие сетевые API
Листинг 1 -2. (продолжение)
{
printf("ERROR: CreateThread: Xd\n", GetLastErrorO);
return;
}
CloseHandle(hThread);
return;
// Функция: ClientThread
//
// Описание:
// Клиентский поток блокирует получение данных от клиентов и
// просто посылает их назад. Это непрерывный цикл, выполняющийся
// пока не будет закрыт сеанс или не произойдет ошибка. Если
// чтение или запись дают ошибку NRC_SCLOSED, сеанс
// корректно завершается и происходит выход из цикла.
//
DWORD WINAPI ClientThread(PVOID lpParam)
PNCB
NCB
char
DWORD
char
pncb = (PNCB)lpParara;
neb;
szRecvBuff[MAX_BUFFER];
dwBufferLen = MAX.BUFFER,
dwRetVal = NRC_GOODRET;
szClientName[NCBNAMSZ+1];
FormatNetbiosName(pncb->ncb_callname, szClientName);
while (1)
{
dwBufferLen = MAX_BUFFER;
dwRetVal = Recv(pncb->ncb_lana_num, pncb->ncb_lsn,
szRecvBuff, AdwBufferLen);
if (dwRetVal != NRC_GOODRET)
break;
szRecvBuff[dwBufferLen] = 0;
printf("READ [LANA=Xd]: 'XsAn", pncb->ncb_lana_num,
szRecvBuff);
dwRetVal = Send(pncb->ncb_lana_num, pncb->ncb_lsn,
szRecvBuff, dwBufferLen);
if (dwRetVal != NRC.GOODRET)
break;
}
printf("Client 'Xs
1
on LANA Xd disconnected\n", szClientName,
pncb->ncb_lana_num);

Г Л А В А 1 Интерфейс NetBIOS 21
Листинг 1-2. (продолжение)
if (dwRetVal != NRC_SCLOSED)
{
// Возникает какая-то другая ошибка; соединение разрывается
//
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBHANGUP;
ncb.ncb_lsn = pncb->ncb_lsn;
ncb.ncb_lana_num = pncb->ncb_lana_num;
if (Netbios(&ncb) != NRC_GOODRET)
<
printf("ERROR: Netbios: NCBHANGUP: Xd\n", ncb.ncb.retcode);
dwRetVal = neb.ncb_retcode;
>
GlobalFree(pncb);
return dwRetVal;
}
GlobalFree(pncb);
return NRC_GO0DRET;
// Функция: Listen
//
// Описание:
// Инициируется асинхронное прослушивание с помощью функции обратного вызова.
// Создается структура NCB для использования обратным вызовом (поскольку она
// должна быть видима глобально).
//
int Listen(int lana, char *name)
{
PNCB pneb = NULL;
pneb = (PNCB)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(NCB));
pncb->ncb_command = NCBLISTEN | ASYNCH;
pncb->ncb_lana_num = lana;
pncb->ncb_post = ListenCallback;
//
// Это ими, с которым клиенты будут соединяться
//
memset(pncb->ncb_name, ' ', NCBNAMSZ);
strncpy(pncb->ncb name, name, strlen(name));
//
// '*' означает, что мы примем клиентское соединение от любого клиента.
// Задавая здесь конкретное имя, мы разрешаем соединение только с
// указанным клиентом.
//
memset(pncb->ncb_callname, ' ',
1   2   3   4   5   6   7   8   9   ...   50


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