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

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


Скачать 2.88 Mb.
НазваниеЭ. Джонс, Д. Оланд
АнкорПрограммирование в сетях Windows.pdf
Дата12.10.2017
Размер2.88 Mb.
Формат файлаpdf
Имя файлаПрограммирование в сетях Windows.pdf
ТипКнига
#9346
страница33 из 50
1   ...   29   30   31   32   33   34   35   36   ...   50

ГЛАВА 12 Кач
377
Листинг 12-1. (продолжение)
const FLOWSPEC flowspec_guaranteed
{17000, iQn
1260,
34000,
QOS_NOT_SPECIFIED,
QOS_NOT_SPECIFIEO,
SERVICETYPE.GUARANTEED,
340,
340};
* '8* )
xvteaefl.soR-
hfve
(
£V
i
ЁвМ
,16.-.
stpeeu •
tevist
' 198
' tt
// Функция: SetReservelnfo
// Описание:
// Для приемников: если требуется подтверждение, это должно быть сделано
// с помощью структуры RSVP_RESERVE_1NFO
void SetQosReserveInfo(QOS «lpqos)
qosreserve.ObjectHdr.ObjectType = RSVP_OBJECT_RESERVE_INFO;
qosreserve.ObjectHdr.ObjectLength = sizeof(RSVP_RESERVE_INFO);
qosreserve.Style = RSVP_DEFAULT_STYLE;
qosreserve. ConfirmRequest = bConfirmResv; ...
qosreserve.NumPolicyElement = 0; 'ievia
w
o-
qosreserve.PolicyElementList = NULL;
(>
, ж-
qosreserve.FlowDescList = NULL; " ' i-
\\
w
!NA0 \\
1
w
w
su blov
Щ
nq
J:x3
lpqos->ProviderSpecific.buf = (char *)&qosreserve;
lpqos->ProviderSpecific.len = sizeof(qosreserve);
return;
:e«H»ot*nO
// Функция: InitQos
// Описание:
// Создает структуры клиента и сервера QOS. Это выделено в отдельную функцию,
// чтобы вы могли изменять запрашиваемые параметры QOS
// и смотреть, как это повлияет на приложение.
\\
\\
\\
\\
void InitQosO
{
clientQos.SendingFlowspec = flowspec_g711;
clientQos.ReceivingFlowspec = flowspec_notraffic;
clientQos.ProviderSpecific.buf = NULL;
clientQos.ProviderSpecific.len = 0;
serverQos.SendingFlowspec = flowspec_notraffic;
\\
-3d
см. след. стр.

378
ЧАСТЬ II Интерфейс прикладного программирования Winsock
Листинг 12-1. (продолжение)
serverQos.ReceivingFlowspec = flowspec_g711;
serverQos.ProviderSpecific.buf = NULL;
serverQos.ProviderSpecific.len = 0;
if (bConfirmResv)
SetQosReserveInfo(&serverQos);
1MNT»NR
// Функция: usage
//
// Описание:
// Печать информации об использовании i*e - void usage(char
{
printf("usage printf("
printf("
printf("
printf("
printf("
printf("
printf("
printf("
printf("
progname)
Xs -q:x -
-q:[b,d,a,
b d
a e
-s
-c:Server-
-w
-r
ExitProcess(-i);
>
s -c:IP\n", progname);
e] When to request QOS\n");
Set QOS before bind or connect\n");
Set QOS during accept cond func\n"); °op
Set QOS after session setup\n");
(
;ор
Set QOS only upon receipt of FD_QOS\n"); op
Act as server\n"); oo
IP Act as client\n"); o*
Wait to send until RESV has arrived\n"); op
Confirm reservation request\n"); =cp
.qi
W
\\
W
\\
A
V.
// Функция: ValidateArgs n-i.li
// Описание:
// Анализирует аргументы командной строки и задает глобальные переменны*,
// чтобы определить, как должно работать приложение void ValidateArgs(int argc, char **argv)
int i;
ив м&отр
// Инициализация глобальных переменных значениями по умолчанию ^ " w o н
iSetQos = SET_QOS_NONE;
bServer = TRUE;
bWaitToSend = FALSE;
bConfirmResv = FALSE;
VarnZ в
for(i = 1; i < argc;
{
if ((argv[i][0] == '-'
\\
\\
\\
\\
\\
\\
\\
\\
•ov
(argv[i][0]

/"ЛАВА 12 Качцрриойаяршвания
379
Листинг 12-1. (продолжение)
switch (tolower(argv[i][1]))
К Rtt(vOK\rO

urea a case ' q ' : // Когда включать QOS
if ( t o l o w e r ( a r g v [ i ] [ 3 ] ) == ' b ' )
objcoum iSetQos = SET_QOS_BEFORE;
o b j v
a
-
- else if ( t o l o w e r ( a r g v [ i ] [ 3 ] ) == '
iSetQos = SET_QOS_DURING;
if {oi*;* else if ( t o l o w e r ( a r g v [ i ] [ 3 ] ) == '
Wof iSetQos = SET_QOS_AFTER;
else if ( t o l o w e r ( a r g v [ i ] [ 3 ] ) == '
П Q, iSetQos = SET_QOS_EVENT;
else usage(argv[O]);
break; di
*« ' case ' s ' : // Сервер printf("Server flag s e t ! \ n " ) ;
bServer = TRUE;
break;
case ' c ' : // Клиент p r i n t f ( " C l i e n t flag s e t ! \ n " ) ;
bServer = FALSE;
if ( s t r l e n ( a r g v [ i ] ) > 3)
d ' ) i A - a ' )
a
1
)
(№'-.
t*1 (
strcpy(szServerAddr, & a r g v [ i ] [ 3 ] ) ;
else w?>
usage(argv[O]); »n fl break;
case 'w': // He отправят» до?
f II пока не поступило
bWaitToSend = TRUE; \1.'Ш -
break; ;jjtJM •
case ' r': j '{№ «
bConfirmResv = TRUE; .38JA4
break; ,Ct » J
d e f a u l t :
usage(argv[O]); ( 0 * • « i . ^ i
break; ,.*.* m4m], « v » 4 t * [ l
p.
сообщение
'0
;te-<
• «boOwb
,«.. fsVwb
;*t>TY8wb
>tJaoH3W
j)
1
>03
sss
J
Ч'-,АЧ n n i
1
JOS'!
4rtO
ы
1 ЙОГ
4 o O " i
RESV
O i l '
j i i j .
C30WQ
= J e i j
a t
<
тлит
{
S\
\\
нт итО \\
aoqH \\
явим \\
\\
Ъ»(*3 -1R0W
^ j
BJtt 'f jooe ftfiOWl
Si) t :
}
return;
) 3,
ibrit«to
// Функция: AbleToSend f)
// Описание:
// Проверяет, могут ли данные быть отправлены на сокете до поступления
си. след. стр.

3 8 0 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Листинг 12-1. (продолжение)
И сообщений RESV. Эта функция проверяет, достаточен ли имеющийся в настоящий
// момент в сети негарантированный уровень для уровней 00S, заданных на сокете.
//
BOOL AbleToSend(SOCKET s) ')' #еео
{ " •
int ret;
DWORD dwCode = ALLOWED_TO_SEND_DATA, Jla.
dwValue,
dwBytes;
ret = WSAIoctl(s, SIO_CHK_QOS, idwCode, sizeof(dwCode),
&dwValue, sizeof(dwValue), &dwBytes, NULL, NULL);
if (ret == SOCKET_ERR0R)
{
pnntf("WSAIoctl() failed: Xd\n", WSAGetLastErrorO);
return FALSE;
>
return (BOOL)dwValue;
// Функция: ChkForQosStatus
//
// Описание'
// Проверяет наличие объекта RSVP_STATUS_INFO и определяет,
// имеются ли в нем нужные флаги
//
DWORD ChkForQosStatus(QOS *lpqos, DWORD dwFlags) o<>eo
{
QOS_OBJECT_HDR *ob]hdr = NULL; "НИ" Ш
RSVP.STATUS INFO «status = NULL; M I J
char" " .bufptr = NULL; «* *S*8*"
ЗД<№
'
BOOL bDone = FALSE; 3d
DWORD obicount = 0, id
*Xuisf ei>
if (lpqos->ProviderSpecific.len == 0) su return 0; >id
{
bufptr = lpqos->ProviderSpecific.buf; ' '
|(
""
в
{
objhdr = (QOS_OBJECT_HDR *)bufptr; {
'91
{
while (ibDone)
{ \\
if (objhdr->ObjectType == RSVP_OBJECT_STATUS_INFO) 4» {
status = (RSVP_STATUS_INFO *)objhdr;
if (status->StatusCode & dwFlags)
return 1;

* j f r 12 Качество обслЦувания
381
Листинг 12-1. (продолжение)
}
else if (objhdr->ObjectType == QOS_OBJECT_END_OF_LIST)
bDone = TRUE;
bufptr += objhdr->Objeel:Length;
objeount += objhdr->ObjectLength;
objhdr = (Q0S_0BJECT_HDR *)bufptr;
en net.tudw
3t en) *•*
u.
if (objeount >= lpqos->ProviderSpeclflc.l»n)
bDone = TRUE; Art ,[>
return 0;
-*
2)V
A3W

1С)
// Функция. HandleClientEvents {
// Г тиси
// Описание'
// Эта функция вызывается функцией Server для обработки событий, fjnx 5
// которые произошли на описателях клиентов SOCKET. Массив <
// сокетов передается вместе с массивом событий и индексом клиенте,i»»oM \\
// получившего оповещение. В функции событие декодируется, \\
// и выполняется соответствующее действие.Г вп)) 1х void HandleClientEvents(SOCKET socks[], HANDLE events[], int index) tn) IJt
WSANETWORKEVENTS ne; !)i char databuf[4096]; ml»
WSABUF wbuf; Л'.кЗТШ iq
DWORD dwBytesRecv, {
dwFlags; •>•
%
" - '
c
"

INK)! \\
int ret,
W K ,
\\
i; Г\\
// Перебор происшедших в сети событий }
// М) It
ret = WSAEnumNetworkEvents(socks[index], events[index], 4ne); »IJ
if (ret == SOCKET.ERROR)
aale pnntf("WSAEnumNetworkEvents() failed: Xd\n",
WSAGetLastErrorO);
return;
// Данные для чтения if ((ne INetworkEvents & FD.READ) == FD_READ)
wbuf.buf = databuf,
см. след. стр.

382 ЧАСТЬ II Интерфейс прикладногсЯЦрограммиротния Winsock
Листинг 12-1. {продолжение) ..«чтих Л июни.
1- -ttm№< Ш! «те
if (ne.iErrorCode[FD_READ_BIT]) isOci
printf("FO_READ error: Xd\n",
ne.iErrorCode[FD_READ_BIT]); -bAtvo •• TJctud
else . (do p+
printf("FD_READ\n"); О_вЬ«)
dwFlags = 0; •-<; tnuootdo) U
ret = WSARecv(socks[index], &wbuf, 1, &dwBytesRecv, itft * *no
&dwFlags, NULL, NULL); {
if (ret == SOCKET_ERROR) j,
J;
;0 muJei
printf("WSARecv() failed: Xd\n", WSAGetLastErrorO);
return; * ',
wbuf.len = dwBytesRecv; \\
printf("Read: %d bytes\n", dwBytesRecv); > ,ф втв V
// Может записывать данные; здесь никакие действия не предпринимаютздхоо л
if ((ne.lNetworkEvents & FD_WRITE) == FD_WRITE)
{ \\
if (ne.iErrorCode[FD_WRITE_BIT]) F4,/iev3tneJ:I3eIbnBH bxov
printf("FD_WRITE error: Xd\n",
ne.iErrorCode[FD_WRITE_BIT]); ;m
else ,-f»eO*]tudeJRb
printf("FD_WRITE\n");
// Клиент закрыл соединение. Закрываем сокет с
// и очищаем структуры данных. jm j n i
/ / Ц
if ( ( n e . l N e t w o r k E v e n t s & FD_CLOSE) == FD.CLOSE)
{ i wt • о s х«йЬР»а«очл ччввчвП \\
i f (ne.iErrorCode[FD_CLOSE_BIT]) \ \
printf("FD_CLOSE e r r o r : J!d\n",w|j8H3Oi)eJneV
1
ne.iErrorCode[FO_CLOSE_BIT]); {Я0Я
else >
pnntf("FD_CLOSE . . . \ n " ) ; .6» ()s?n«v3)!iowj' 'А8У(" • U t q closesocket(socks[index]); iCO^o- J i t
WSACloseEvent(events[index]); i j e i
<
socks[index] = INVALID_SOCKET;
//
// Удаляем элемент клиентского сокета из массива и
// сдвигаем оставшиеся элементы к началу массива.
//
for(i = index; i < MAX_C0NN - 1;

Г Л А В А 12 Качество обслуживания 383
Листинг 12-1. (продолжение) , . . .
socks[i] = socks[i + 1]; \\
nConns-; »' «ей S\
} i«qr 'Л
^ II Получено событие FD_QOS. Это может иметь разные значения. \\
if ((ne.lNetworkEvents & FD_QOS) == FD.QOS)
char
ia
buf[QOS_BUFFER_SZ]; >'.i -
QOS *lpqos = NULL; s {
DWORD dwBytes;
\\
i
f
(
n
e
.
i
E
r
r
o
r
C
o
d
e
[
F
D
_
Q
O
S
_
B
I
T
]
)
J
q
e
o
o
A
b
P
K
i
a
v
i
B
,
.
Ь
>
А
Н
\
$
\
\
p
r
i
n
t
f
(
"
F
D
.
Q
O
S
e
r
r
o
r
:
X
d
\
n
"
,
'
\
^
n
e
.
i
E
r
r
o
r
C
o
d
e
[
F
D
_
Q
O
S
_
B
I
T
]
)
;

&
>
.
-
w
.
-
i
n
O
\
\
3i e l s e '" '""' 1%Щ4 *&!№( о , . \\
G* printf("FD_QOS\n"); ** к 'е<»м>, \\
» ^ t тес, \\
lpqos = (QOS Obuf; «a** , 'tie \\
**'' lpqos->ProviderSpecific.buf = &buf[sizeof(QOS)]; •** 0- lpqos->ProviderSpecific.len = sizeof(buf) - sizeofCOW); \\
• w
ret = WSAIoctl(socks[index], SIO.GET.QOS, NULL, О, <П ь \\
buf, QOS.BUFFER.SZ, &dwBytes, NULL, NULL); «>*- на '
v
\
if (ret == SOCKET.ERROR)
printf("WSAIoctl(SIO_GET_QOS) failed M^J4, ,ь .-'I 1J
WSAGetLastErrorO); I 4U8A8WJ i нЧЛ
return; > e"
1
*3
} ,0 > #0
PrintQos(lpqos); ;•- <ШвЗА(ТЗ> Of
//
э я
// Проверим, имеется ли настройка для приема только событий FD.QOS.я1
// Если это так, то нужно вызвать QOS на соединении сейчас; иначе
// клиент никогда не получит сообщение RESV. f*
if (iSetQos == SET_QOS_EVENT)
lpqos->ReoeivingFlowspec.ServiceType =
serverQos.ReceivingFlowspec.ServiceType;
ret = WSAIoctl(socks[index], SIO.SET.QOS, lpqos, i
dwBytes, NULL, 0, AdwBytes, NULL, NULL); "Ц
if (ret == SOCKET.ERROR) **
printf("WSAIoctl(SIO_SET_QOS) failed: Xd\n",
WSAGetLastErrorO);
return;
см. след. стр.

384 ЧАСТЬ II Интерфейс прикладного программирования Winsock ,
Листинг 12-1. {продолжение) ;
II
I/ Изменить ISetQos, чтобы не пришлось снова задавать QOS Dtt
// при получении еще одного события FD_QOS {
// с омйиупоП \\
lSetQos = SET_QOS_BEF0RE; \\
} »JH -<И
£
ел)) П
return; i&rto
> , гоо
// Функция: SrvCondAccept en) ft
II Описание.
// Это условная функция для WSAAccept. Поставщик службы QOS [е
// имеет ограничение: значения QOS, передаваемые сюда, не надежны,
// поэтому параметр SET_QOS_DURING бесполезен, если мы не вызовем
// SIO_SET_QOS с нашими значениями (в противоположность значениям,
// запрашиваемым клиентом, поскольку те значения должны быть
// возвращены в IpSQOS).
// Заметьте' если в Windows 98 значение IpSQOS отлично от NULL,
// вы должны задать некоторые значения QOS (с помощью SIO_SET_QOS)
// в условной функции, иначе функция WSAAccept не отработает.
mt CALLBACK SrvCondAccept(LPWSABUF lpCallerld, )
LPWSABUF lpCallerdata, LPQOS IpSQOS, LPQOS lpGQOS,
LPWSABUF lpCalleeld, LPWSABUF lpCalleeData, GROUP *g,
DWORD dwCallbackData) j m u J M
DWORD dwBytes = 0 ; \
SOCKET s = (SOCKET)dwCallbackData;
,£»OMl)eoOJnJ"i4
SOCKADDR_IN client;
C
-
H<
"' * VurtJ,
lnt ret; 'ioimen
твтн .«Nqeeo^fl \\
T ,XJt1 OT« NftOi \\
if (nConns == MAX_C0NN) -аюями тнвкйя \\
return CF_REJECT; \\
memcpy(&client, lpCallerId->buf, lpCallerId->len);
printf("Client request: Xs\n", inet_ntoa(client.sin_addr));
»o if (lSetQos == SET_QOS_EVENT)
( LA3W •» Jet.
printf("Setting for event!\n"); asjyOwb serverQos. SendingFlowspec. ServiceType |= rei) tJ:
SERVICE_NO_QOS_SIGNALING, }
serverQos ReceivingFlowspec.ServiceType |=
SERVICE_NO_QOS_SIGNALING;
ret = WSAIoctl(s, SIO_SET_QOS, &serverQos,
sizeof(serverQos), NULL, 0, &dwBytes, NULL, NULL);

«<1фо Г Л А В А 12 Качество обслуживания
385
Листинг 12-1. {продолжение)
if (ret == SOCKET.ERROR)
{ »J
printf("WSAIoctl() failed: Xd\n"
WSAGetLastErrorO);
return CF.REJECT;
return CF_ACCEPT;
jl m
re*
W8_80O_T38 «
[0]э«>
coOJs>
ж » • »
г:
О '
tt
// Функция: Server
// q
// Описание'
// Эта подпрограмма сервера обрабатывает входящие клиентские соединения.
// Сначала она задает прослушивающий сокет, затем задает Q0S ,
// в нужный момент времени и ожидает входящих клиентов и события. }-ц
// 7 с-П
void Server(SOCKET s)
JIlArt
SOCKET sc[MAX_CONN + 1 ] ;
WSAEVENT hAllEvents[MAX_C0NN+1];
SOCKADDR.IN local,
client;
mt clientsz,
ret,
I;
DWORD dwBytesRet;
WSANETWORKEVENTS ne;
// Инициализация массивов неверными значениями.
//
for(i = 0; l < MAX_C0NN+1;
тх
"ЙЗ.ГЗХООЗ
hAHEvents[i] = WSA_INVALID_EVENT;
sc[i] = INVALID_SOCKET;
(Г) eIJtrtw
jH\o4ttBV(AC« • tei
«* Jet) tt
i
// Нулевой элемент массива будет прослушивающим сокетом. - • t))
//
hAllEvents[0] = WSACreateEvent();
sc[0] = s; \
nConns = 0 , /3).
local.sin_family = AF_INET;
local sin_port = htons(5150);
local sm_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, (SOCKADDR *)&local, sizeof(local)) == SOCKET.ERROR)
см след стр

386 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Листинг 12-1. (продолжение) !
{ ^
printf("bind() failed: Xd\n", WSAGetLMttErrorO); )
return; 1>Ш
l i s t e n ( s , 7);
{
if (iSetQos == SET_Q0S_BEF0RE)
{ >MJ,
ret = WSAIoctl(sc[0], SI0_SET_Q0S, &serverOos, i sizeof(serverQos), NULL, 0, &dwBytesRet, NULL, NULL);
if (ret == SOCKET_ERR0R) \
printf("WSAIoctl(SIO_SET_QOS) failed: Xd\n", j
WSAGetLastErrorO); 1 .экквзмпО \
return; в <
l
"М)пдоп exfi \
v
} vr, .jfo eiweHO
p r i n t f ( " S e t QOS on l i s t e n i n g socket:\n"); »<• Лингун в
PrintQos(&serverQos); >,
4
if (WSAEventSelect(sc[O], hAHEvents[0], FD_ACCEPT) == ТЗЯ00 '
S0CKET_ERR0R)
4
. TH3V1A-'.
{ MI_»fl0AX00s;
printf("WSAEventSelect() f a i l e d : Xd\n", WSAGetLastErrorO);
return; Jnt while (1) 0Jv8w6
{ - зти ret = WSAWaitForMultipleEvents(nConns+1, hAHEvents, FALSE,
WSA_INFINITE, FALSE);
if (ret == WSA_WAIT_FAILED)
{ Ц > jt ;O «
printf("WSAWaitForMultipleObject() failed: Xd\n", >
WSAGetLastErrorO);
return;
} (
if ((i = ret - WSA_WAIT_EVENT_O) > 0) // Сетевое событие клиента I \\
HandleClientEvents(sc, hAHEvents, i); \\
else
{ "• .
ret = WSAEnumNetworkEvents(sc[0], hAllEvents[0], » t
&ne);
if (ret == S0CKET_ERR0R) <1.'
{
printf("WSAEnumNetworkevents() failed: Xd\n",
WSAGetLastErrorO);
return;

Г Л А В А 12 Качество обслуживания
387
Листинг 12-1. (продолжение') , , .. тнк
lU
if ((ne.lNetworkEvents & FD_ACCEPT) == FD_ACCEPT)Jnno
, { iKI.en) Tt if (ne.iErrorCode[FD_ACCEPT_BIT]) >«nJh<}
,, ,'« printf("FO_ACCEPT error: Xd\n", i
, ne.iErrorCode[FD_ACCEPT_BIT]); {
м else ,i >.re.
printf("FD_ACCEPT\n"); {
ft
1 clientsz = sizeof(client); \\
} sc[++nConns] = WSAAccept(s, (SOCKADDR *)&client, nut 10 .м«;,м
¥» \\
elf-, if u &clientsz, SrvCondAccept, sc[nConns]); \\
Ipqo» if (sc[nConns] == SOCKET_ERROR) :»b->bj^,Q \\
{
ты *т&е< п.-, "до г
\\
printf("WSAAccept() failed: Xd\n", •wwrede^o и чни*ъц« \\
С 1 Ч Ш WSAGetLastErrorO); \\
;ь nConns-; (« ГЭЛ008)}г»чН.< uiov client return; p« j* }
hAHEvents[nConns] = WSACreateEvent();
n Sleep(10000); ^
if in if (iSetQos == SET_QOS_AFTER) J
{ { ^
pt ret = WSAIoctl(sc[nConns], SI0_SET_Q0S,
•t &serverQos, sizeof(serverQos), NULL, 0,
!
<
} &dwBytesRet, NULL, NULL); •"> ^i
if (ret == S0CKET_ERR0R)
. ,ir, ' printf("WSAIoctl() failed: Xd\n"
iir WSAGetLastErrorO);
if return; Л',".
>
800
втийтакйштжш
(JJUH •• tn»«2rt) TI
ret = WSAEventSelect(sc[nConns],
hAHEvents[nConns], FD_READ | FD_WRITE
FD_CLOSE | FD.QOS);
if (ret == S0CKET_ERR0R)
*« printf("WSAEventSelect() failed: Xd\n",
WSAGetLastErrorO);
return;
•M№
if (ne.lNetworkEvents 4 FD_CLOSE)
printf("FD_CLOSEn");
if (ne.lNetworkEvents & FD_READ)
pnntf("FD_READn");
if (ne.lNetworkEvents & FD.WRITE)
см. след. стр.

388
ЧАСТЬ II Интерфейс прикладного программирования Winsock
Листинг 12-1. (продолжение)
printf("FD_WRITEn"); ШОА.С
if (ne.lNetworkEvents & F t y M D '
pnntf("FD_QOS\n");
return;
// Функция: Client
// времени и обрабатывает входящие события.
st
// Описание:
// Подпрограмма клиента инициирует соединение, задает QOf в нужны! момент
f 'Ч
void Client(SOCKET s)
{
SOCKADDR_IN
WSABUF
DWORD
HANDLE
int char
QOS
server,
local; ;()tfievls wbuf;
dwBytes,
dwBytesSent,
dwBytesRecv,
dwFlags;
hEvent; . ,i- ret, i; ,j,.
databuf[DATA_BUFFER_SZ]; ТЗЖ»
*lpqos;
WSANETWORKEVENTS ne; Ш о о 1 А Г
hEvent = WSACreateEvent();
if (hEvent === NULL)
ti
Him,
printf("WSACreateEvent() failed: Xd\n", WSAGetLastErrorfJ};
return;
lpqos = NULL;
if (iSetQos == SET_Q0S_BEF0RE)
local.sin_family = AF_INET; j«l local.sin_port = htons(O);
v e
local.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, (SOCKADDR *)&local, sizeof(local)) ==
SOCKET_ERROR)
printf("bind() failed: Xd\n", WSAGetLastErrorO);
return;
t
Tt

ГЛАВА 12 Качество обслуживания , 389
Листинг 12-1. (продолжение) *>,
ret = WSAIoctl(s, SIO_SET_QOS, iclientQos,
sizeof(clientQos), NULL, 0, &dwBytes, NULL, NULL);
if ( r e t == SOCKET_ERROR) и
printf("WSAIoctl(SIO_SET_QOS) f a i l e d : Xd\n",
WSAGetLastErrorO); и дт/
return; ' ff-2 1HNT УШ
fud
\Q
" feft
> ( Г )
e l s e if ( i S e t Q o s == SET_QOS_DURING)
l p q o s = & c l i e n t Q o s ; . j ^
e l s e if ( i S e t Q o s == SET_QOS_EVENT) »,
{ ) It
clientQos.SendingFlowspec.ServiceType |= }
SERVICE_NO_Q0S_SIGNALING; ;
п
Ьц
clientQos.ReceivingFlowspec.ServiceType |= J
SERVICE_NO_QOS_SIGNALING;
ret = WSAIoctl(s, SI0_SET_Q0S, &clientQos,
sizeof(clientQos), NULL, 0, idwBytes, NULL, NULL); ,AStt * Jet if (ret == S0CKET_ERR0R) U,»,%J%i) It
printf("WSAIoctl() failed: Xd\n", WSAGetLastErrorO)»
n bq return;
server.sin_family = AF_INET;
server.sin_port = htons(5150);
server.sin_addr.s_addr = inet_addr(szServerAddr);
'nitq
printf("Connecting to: Xs\n", lnet_ntoa(server.sin_addr));
ret = WSAConnect(s, (SOCKADDR *)4server, sizeof(server),
NULL, NULL, lpqos, NULL);
if (ret == S0CKET_ERR0R)
printf("WSAConnect() f a i l e d : Xd\n", WSAGetLastErrorO)! ми return; зэт) Itt
Ti
}
т t i - '\
ret = WSAEventSelect(s, hEvent, F0_READ
FD_CLOSE | FD_Q0S);
if (ret == S0CKET.ERR0R)
FD_WRITE
p r i n t f ( " W S A E v e n t S e l e c t ( ) f a i l e d : Xd\n", WSAGetLastErrorO);
см. след. стр.

3 9 0 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Листинг 12-1. (продолжение) \.>
return;
} ttiii ,800.1
ФЙ ,0 ,J.Win
wbuf.buf = databuf; .'
wbuf.len = DATA_BUFFER_SZ;
3O0_T3CJ
!
^
memset(databuf, '#', DATA_BUFFER_SZ); К Г Пш»,1
databuf[DATA BUFFER_SZ-1] = 0; /
Г <
while (1) {
{ "iKI eo0?u3i) tJt eel»
ret = WSAWaitForMultipleEventsd, ShEvent, FALSE, »ila* • sop<>f
WSA_INFINITE, FALSE);
if (ret == WSA_WAIT_FAILED)
printf("WSAWaitForMultipleEventsO failed: Xd\n",iS)_30IVR33
WSAGetLastErrorO); «- return; ; IVR38
ret = WSAEnumNetworkEvents(s, hEvent, &ne); ,0ТПч
if (ret == SOCKET_ERROR) > .ТЗЛ •• tei) ft
printf("WSAEnumNetworkEvents() failed: Xd\n",*oIA8«'')tJnii(}
WSAGetLastErrorO); ;tnut*i
return; (
if (ne.lNetworkEvents & FD_READ)
{ -ТЭИ13А * yJleet.nits.i»vi9c'
lf (ne.iErrorCode[FD_READ_BIT]) rri>enoJrt printf("FD_READ error: Xd\n",
'£ » ibca_
ne.iErrorCode[FD_READ_BIT]);
else printf("FD_READ\n");
wbuf.len = 4096; UtW .JJl'H
dwFlags = 0; '02 »*
Зет) t."
ret = WSARecv(s, &wbuf, 1, idwBytesRecv, &dwFlags, '>
NULL, NULL); 4
if (ret == SOCKET.ERROR)
{ < <
printf("WSAReov() failed: Xd\n",
WSAGetLastErrorO);
return; ta\
) Л
printf("Read: Xd bytes\n", dwBytesRecv);
wbuf.len = dwBytesRecv;
ret = WSASend(s, &wbuf, 1, &dwBytesSent, 0, NULL,

ГДАВА12
Листинг 12-1. {продолжение') шЫхтук) . M l 1и*г>зд>
NULL); v, -;,
}
if ( r e t == SOCKET.ERROR) эа • | 3 1 , « г ) П
printf("WSASend() f a i l e d :
WSAGetLastErrorO);
return; 3iOJL' щ
p r i n t f ( " S e n t : %d bytes\n", dwBytesSent)!
s v < w if (ne.lNetworkEvents & FD.WRITE)
{ 2 t lie*.' »ni i f (ne.iErrorCode[FD_WRITE_BIT])
printf("FD_WRITE error: Xd\n", CO t*rta ne.iErrorCode[FD_WRITE_BIT]); a 800
else $Jv0wb QfiOV^Ci printf("FD_WRITE\n");
) я >
. v<.3ffv3»Rd J0O8
if (! bWaitToSend) ,а„800.fl4]etrJ3icn3l .wbuf.buf = databuf; >t > S< ')f3
wbuf.len = DATA_BUFFER_SZ;
// Если сеть не может обеспечить пропускную способность,
// не отправлять данные. {
// в if (!AbleToSend(s)) *?„,
printf("Network is unable to provide " }) к aopqf
"sufficient best-effort bandwidth\n"jj»j( n з$г
printf("before the reservation " jd
"request is approved\n"); « jei) U
1 > }
for(i = 0 ; i < 1; i++)
ret = WSASend(s, &wbuf, 1, idwBytesSent, 0, {
NULL, NULL); го,- sintt4
if (ret == SOCKET.ERROR) \\
{ ,d ЩП \\
printf("WSASend() failed: Xd\n",
WSAGetLastErrorO);
return; ,^,
printf("Sent: Xd bytes\n", dwBytesSent);
}
}
if (ne.lNetworkEvents & FD.CLOSE)
см. след. стр.

3 9 2 ЧАСТЬ II Интерфейс приклаМЫ"о пропмммюоаания Winsock
Листинг 12-1. {продолжение)
{ и if (ne.iErrorCode[FD_CLOSE_BIT]> («ОЙЯг ) П
printf("FD_CLOSE error: Xd\n\ )
ne.iErrorCode[FD_CLOSE_BlTJf; ()k<
else t ) i o n a j printf("FD_CLOSE . . . \ n " ) ; r closesocket(s); {
WSACloseEvent(hEvent); ^fVtwb . 'n/893\'d Ы :+i«e f«Jnq return; {
if (ne.lNetworkEvents & FD_QOS) }
char
QOS
DWORD
BOOL
buf[QOS_BUFFER_SZ];
•lpqos = NULL;
dwBytes;
bRecvRESV = FALSE;
i f (ne.iErrorCode[FD_QOS_BIT])
{
printf("FD_QOS error: Xd\n",
ne.iErrorCode[FD_QOS_BIT]); i
if (ne.iErrorCode[FD_QOS_BIT] == WSA_Q0S_RECEIVER5)
bRecvRESV = TRUE;
>
else
printf("FD_QOS\n"); ( dW.
lpqos = (QOS *)buf; >ili4
ret = WSAIoctKs, SI0_GET_Q0S, NULL, 0,
buf, QOS_BUFFER_SZ, idwBytes, NULL, NULL);
if (ret == SOCKET_ERROR)
{ {
printf("WSAIoctl(SIO_GET_QOS) failed: Xd\n",
WSAGetLastErrorO); Het
return; }
} ' •'
PrintQos(lpqos);
// '»
// Проверить, возвращен ли обьект состояния в структуре QOS,
// которая может также содержать флаг WSA_QOS_RECEIVERS
//
if (ChkForQosStatusdpqos, WSA_QOS_RECEIVERS))
bRecvRESV = TRUE;
if (iSetQos == SET_Q0S_EVENT)
{
lpqos->SendingFlowspec.ServiceType =
clientQos.SendingFlowspec.ServiceType;
ret = WSAIoctKs, SI0_SET_Q0S, lpqos, dwBytes,

Г Л А В А 12 Качество обслуживания
393
Листинг 12-1. {продолжение)
NULL, 0, idwBytes, NULL, NULL);
if (ret == SOCKET_ERROR)
pnntf("WSAIoctl(SIO_SET_QOS) failed: Xd\n"
WSAGetLastErrorO);
return;
// Изменить lSetQos, чтобы не пришлось снова включать QoS
// при приеме еще одного события FD_QOS.
//
iSetQos = SET_QOS_BEFORE;
if (bWaitToSend && bRecvRESV)
{
wbuf.buf = databuf;
wbuf.len = DATA_BUFFER_SZ;
for(i = 0 ; i < 1;
ret = WSASend(s, &wbuf, 1, &dwBytesSent, 0,
NULL, NULL);
if (ret == SOCKET_ERROR)
{
printf("WSASend() failed: Xd\n",
WSAGetLastErrorO);
return;
}
printfC'Sent: Xd bytes\n", dwBytesSent);
A
>
return;
// Функция: main
//
// Описание:
// Инициализирует Winsock, анализирует аргументы командой строки, создает
// сокет QOS TCP и вызывает соответствующую подпрограмму обработки
// в зависимости от переданных аргументов
//
int main(int argc, char **argv)
{
WSADATA wsd;
см. след. стр.

3 9 4 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Листинг 12-1. (продолжение) „ Л-
WSAPR0T0C0L_INF0 *pinfo = NULL; • ,jf
SOCKET s; ?H00# » ti
}
// Анализ командной строки . Ц5У
ValidateArgs(argc, argv); , J j f r t if (WSAStartup(MAKEW0RD(2,2), &wsd) != 0)
< I
printf("Unable to load Winsock: Xd\n", GetLastErrorO); \\
return - 1 ; \ \
> w pinfo = FindProtocolInfo(AF_INET, SOCK_STREAH, IPPR0T0_TCP, \\
XP1_Q0S_SUPP0RTED); .,8x if (!pinfo) {
printf("unable to find suitable provider!\n");
return -1; '• } i.ttfdw printf("Provider returned: Xs\n", pinfo->szProtocol);
s = WSASocket(FR0M_PR0T0C0L_INF0, FROM_PROT0COL_INFO,
FROM_PROTOC0L_INFO, pinfo, 0, WSA_FLAG_OVERLAPPED); Щ-
if (s == INVALID_SOCKET)
< t i printf("WSASocket() failed: Xd\n", WSAGetLastError())j return - 1 ;
v
,, j ,
4
> лот A f t
InitQosO;
i f (bServer)
Server(s);
else
C l i e n t ( s ) ; tctUMQJttr Ш ) f e l l e d ; M\«"
closesocket(s);
WSACleanupO;
return 0;
Одноадресный UDP ^
v
*
Одноадресный UDP предоставляет больше возможностей по сравнению с
TCP. Пример UDP на прилагаемом компакт-диске называется Qosudp.c. Он объединяет отправитель и приемник. Отправитель может двумя способами сообщить поставщику службы QoS о том, куда следует отправлять данные.
Помните: для инициирования сеанса RSVP требуется адрес партнера. Его мож- но определить с помощью вызова WSAConnect или SIO_SET_QOS с объектом
QOSJDESTADDR.

Г Л А В А 12 Качество обслуживания 395
Пример одноадресного UDP также использует параметр, сообщающий,
когда необходимо включить QoS. Если пользователь хочет задать QoS до при- вязки или подключения, используется ioctl-команда SIO_SET_QOS с объектом
QOSDESTADDR, Если принято решение включить QoS во время выполне- ния условной функции WSAAccept, параметры QoS указываются в вызове
WSAConnect. Если пользователь желает включить QoS после установления сеанса, WSAConnect вызывается без QoS, a SIO_SET_QOS — позднее без объекта
QOSJDESTADDR. Наконец, чтобы задать QoS только после получения уведом- ления о событии FD_QOS, SIO_SET_QOS вызывается с объектом QOSJDES-
TADDR, но над флагом SERVICE_QOS_NOJSIGNALING и значением поля Ser-
viceType структуры FLOWSPEC нужно выполнить логическую операцию ИЛИ.
У принимающей стороны немного параметров. Различные флаги, указы- вающие, когда задавать QoS, здесь не применяются. QoS задают до приема данных или приемник ожидает, когда произойдет событие FD_QOS. Такое поведение вызвано тем, что UDP не получает никакого запроса на подклю- чение: QoS не задана во время выполнения функции приема или после ус- тановления сеанса. Приемник имеет также возможность определить другой стиль фильтра, например, фиксированный или общий явный. Если задан другой фильтр, с параметром -г:1Р должен быть передан IP-адрес. Функция
SetQosReceivers заполняет структуру RSVPJIESERVEJNFO структурой RSVP_
FILTERSPEC, определяющей IP-адрес отправителя. Задать номер порта отпра- вителя особенно важно. Это означает, что приемник должен знать IP-адрес каждого отправителя и номер порта, к которому он привязан.
Приемник может также использовать WSAConnect, чтобы сопоставить IP- адрес отправителя сокету. Но поскольку приемник UDP вправе задать разные стили фильтров и количество отправителей, WSAConnect использовать нельзя.
Помните: если WSAConnect применяется для сопоставления IP-адреса конеч- ной точки, операции передачи и приема правомерны только для этого парт- нера, и ему должна быть сопоставлена QoS.
Пример с одноадресным UDP похож на пример с TCP. Различны лишь способы включения QoS на сокете. Приложения UDP требуют от отправи- теля задания IP-адреса приемника для вызова RSVP, а приложения TCP дела- ют это по умолчанию, в вызове соединения. Цикл событий для обоих при- ложений почти одинаков. Не забудьте: для приложений UDP сокет должен быть привязан локально до включения любой QoS (передачи или приема) с
SIO_SET_QOS, когда WSAConnect не используется. Допускается привязка к
INADDRANYn порту 0, а также использование специального IP-адреса и пор- та. WSAConnect выполняет неявную привязку, поэтому если QoS включается на этом этапе, делать явную привязку заранее не нужно.
Многоадресный UDP
Последний пример — это многоадресная QoS (файл Qosmcast.c на компакт- диске). Его главная функция — WSAJoinLeaf, которую приложение должно вызвать, чтобы присоединиться к группе многоадресной рассылки. После этого оно может также передавать параметры QoS. В примере с многоадрес- ной рассылкой использованы те же параметры, что и в примере с одноад-

3 9 6 ЧАСТЬ II Интерфейс прикладного программирования Winsock ресной рассылкой TCP Вы можете выбрать момент задания QoS на сокете
Если решите сделать это во время выполнения условной функции приема,
QoS будет передана в вызов WSAJoinLeaf Или задайте QoS с помощью вызо- ва WSAIoctl с SIO_SET_QOS
Один из параметров для приемника позволяет пользователям задать фик- сированный или общий явный фильтр Помните многоадресный UDP по умолчанию использует фильтр, содержащий метасимволы Другой тип фильтра применим только к приемникам, и если требуется именно это, адрес каждого отправителя задается в параметре командной строки -г SenderIP
Пользователи могут задавать фильтры через параметр -f с режимом se — для общего явного, и с режимом ff — для фиксированного фильтра
Параметр -т отправитель и получатель используют, чтобы задать мно- гоадресную группу, к которой следует присоединиться Этот параметр мож- но задавать несколько раз и присоединяться к любому количеству групп
Параметр -s указывает, чго программа функционирует как отправитель
Параметр -w сообщает отправителю о необходимости подождать уведом- ления WSA_QOS_RECEIVERS цо передачи данных Наконец, параметр -q оп- ределяет, когда включать QoS, причем независимо от выбранного момента,
сокет локально привязывается к порту 5150
На самом деле, можно выбрать любой порт или задать 0, чтобы порт был выбран автоматически Между тем, если получатель задает фиксированный или общий явный фильтр, он должен также сообщить IP-адрес и порт отпра- вителя Для простоты мы используем фиксированный порт В отличие от одноадресного UDP получатель не должен локально привязывать порт, что- бы задать QoS — ведь WSAJoinLeaf неявно привязывает сокет, если он еще не привязан А вот использовать вместо WSAJoinLeaf команды сокета 1P_ADD_
MEMBERSHIP и IPJDROP MEMBERSHIP нельзя — тогда параметры QoS не бу- дут применяться к сокету
В этом примере QoS включается почти так же, как и в случае с одноадрес- ным UDP, поэтому мы не будем подробно на этом останавливаться Глав- ное — представлять, как инициируется сеанс RSVP и что нужно для генери- рования сообщений PATH и RSVP
ATM и QoS
Как уже упоминалось, служба QoS изначально доступна в сетях ATM Windows
2000 и Windows 98 (с SP1) поддерживают программирование ATM из Win- sock QoS изначально доступна в сети ATM, так что собственные сеть, при- ложение и компоненты политики, необходимые для QoS через IP, не требу- ются Это же относится и к службе управления доступом (Admission Control
Service, ACL) и к протоколу RSVP Вместо этого, коммутатор ATM выделяет пропускную способность и предотвращает ее избыточное использование
Помимо этих различий функции Winsock API работают с QoS в сетях ATM
немного иначе, чем с QoS поверх IP Первое главное отличие — запрос про- пускной способности QoS обрабатывается, как часть запроса соединения
Это отличается от QoS поверх IP — сеанс RSVP устанавливается отдельно от соединения Если запрос пропускной способности отклонен в ATM, соеди-

Г Л А В А 12 Качество обслуживания
397
нение установлено не будет Отсюда следует, что оба базовых поставщика
ATM требуют соединения В результате не возникает проблем с заданием уровней QoS для сокета, не требующего соединения, и последующим опре- делением конечной точки соединения
Второе и главное отличие — только одна сторона задает параметры QoS
для соединения Это значит, что если клиент хочет задать QoS на соедине- нии, отправляющая и принимающая структуры FLOWSPEC будут назначены внутри структуры QoS, переданной функции WSACOnnect Эти значения за- тем применятся к соединению (в QoS через IP отправитель запрашивает определенные уровни QoS и получатель осуществляет необходимое резер- вирование) Кроме того, прослушивающий сокет может иметь QoS, заданную с помощью WSAIoctl и SIO_SET_QOS, и ее параметры будут применены к лю- бому входящему соединению Иными словами, QoS должна быть включена во время настройки соединения, нельзя включить QoS на уже установленном соединении
Отсюда следует, что если QoS включено для соединения, нельзя повторно согласовать параметры QoS с помощью вызова WSAIoctl и S1O_SET_QOS Задан- ные параметры QoS останутся неизменными до окончания соединения
Помните RSVP отсутствует, и не происходит никакого оповещения Это означает, что никакие флаги состояния не генерируются, до окончания со- единения не происходит никаких уведомлений или событий
п (
Резюме
; £ ,
QoS предлагает мощные возможности приложениям, требующим гаранти- рованного уровня сетевого обслуживания Установка соединения QoS дос- таточно сложна, но это не должно вас останавливать Самое важное — знать,
как и когда генерируются сообщения RSVP
i
J
! I

Г Л А В А 1 3
Простые сокеты
Простой сокет обеспечивает доступ к базовому протоколу передачи. В этой главе мы рассмотрим использование простых сокетов для моделирования таких утилит IP, как Traceroute и Ping. Простые сокеты также можно приме- нять для манипулирования информацией в заголовке IP. Мы обсудим толь- ко протокол IP, поскольку большинство остальных протоколов (кроме
ATM) вообще не поддерживает простые сокеты. Все простые сокеты созда- ются с использованием типа сокета SOCKRAW и поддерживаются сейчас только Winsock 2, так что ни Windows СЕ, ни Windows 95 (без обновления
Winsock 2) не могут их использовать.
Работа с простыми сокетами требует достаточно глубокого знания струк- тур протоколов. Подробно в книге мы их рассматривать не будем, а обсудим лишь Internet Control Message Protocol (ICMP), Internet Group Management
Protocol (IGMP) и User Datagram Protocol (UDP). С помощью ICMP утилита
Ping определяет, эффективен ли маршрут к узлу и отвечает ли тот на запро- сы. Разработчикам часто требуется программно определить рабочее состоя- ние и доступность компьютера.
IGMP используется многоадресным вещанием IP для оповещения марш- рутизаторов о присоединении узлов к группе; недавно в большинство плат- форм Win32 была добавлена функция поддержки IGMP версии 2. Но вы мо- жете и отправить собственные IGMP-пакеты, чтобы прекратить членство в группе. Мы рассмотрим протокол UDP вкупе с параметром сокета IPHD-
RINCL в качестве способа, позволяющего сделать это.
Для всех трех перечисленных протоколов мы обсудим только аспекты,
необходимые для полного объяснения кода. Для более подробного изучения обратитесь к книге Ричарда Стивена'.
Создание простого сокета
Первый шаг в использовании простого сокета — его создание. Вы можете сде- лать это, как с помощью функции socket, так и WSASOCKET. Как правило, за- писи каталога в Winsock для IP, которым соответствует тип сокета SOCK RAW,
отсутствуют, но это вам не помешает, так как означает только невозможность создать простой сокет с помощью структуры WSAPROTOCOLJNFO. (Инфор- мация о перечислении записей протокола с параметром WSAEnumProtocols и
W.Richard Steven. TCP/IP Illustrated. Vol. 1. Addison- Wesley. 1994.

Г Л А В А 13 Простые сокеты 399
структурой WSAPROTOCOLJNFO содержится в главе 5.) Для создания сокета вам придется самостоятельно пометить флаг SOCK_RAW. Приведенный далее отрывок кода поясняет создание простого сокета с использованием ICMP в качестве базового IP-протокола.
SOCKET s;
s = socket(AF_INET, S0CK_RAW, IPPROTO_ICMP);
// Или s = WSASocket(AF_IKET, SOCK.RAW, IPPROTOJXMP, NULL, 0. ,
4
WSA_FLAG_OVERLAPPED); **
П
'
if (s == INVALID_SOCKET)
< "
T i
// При создании сокета возвращена ошибка '
}
г
Поскольку простые сокеты позволяют манипулировать базовым транспор- том, их можно использовать для причинения вреда. Поэтому в Windows NT
их вправе создавать только члены группы Administrators. Windows 95 и Win- dows 98 не налагают никаких ограничений.
Чтобы обойти систему безопасности Windows NT, блокируйте проверку защиты на простых сокетах, создав следующую переменную системного реестра и присвоив ей 1 как значение типа DWORD.
HKEY_LOCAL_MACHINE\System\CurrentControlSet •• < h
\Se rvices\Afd\Pa ramete rs\DisableRawSecu rity
После изменения системного реестра перезагрузите компьютер.
В этом примере кода мы использовали протокол ICMP, но вы можете так- же применить IGMP, UDP, IP или упрощенный IP, задействовав флаги IPPRO-
TOJGMP, IPPROTOJJDP, IPPROTOJP или IPPROTORAW соответственно. Од- нако есть ограничение: в Windows NT 4, Windows 98 и Windows 95 (с Win- sock 2) при создании простых сокетов допустимо применение только IGMP
и ICMP. Флаги протокола IPPROTOJJDP, IPPROTOJP и IPPROTO_RAW требу- ют использования параметра сокета IPHDRINCL, не поддерживаемого эти- ми платформами. Windows 2000, однако, поддерживает этот параметр, а по- тому можно манипулировать самим заголовком IP (IPPROTOJRAW), заголов- ком TCP (IPPROTOJTCP) и заголовком UDP (IPPROTOJJDP).
Создав простой сокет с соответствующими флагами протокола, исполь- зуйте его описатель в вызовах передачи и приема данных. При создании простых сокетов заголовок IP будет добавляться в данные, возвращаемые после приема каждой порции информации, независимо от того, задан ли па- раметр IPHDRINCL.
Протокол ICMP
ICMP — инструмент передачи сообщений между узлами. Большинство сооб- щений ICMP касается ошибок взаимодействия узлов, остальные — применя- ются для опроса узлов. Этот протокол использует IP-адресацию, поскольку

400
ЧАСТЬ II Интерфейс прикладного программирования Winsock встроен в дейтаграмму IP. Поля сообщений ICMP показаны на рис 13-1. Со- общение ICMP заключено в заголовок IP.
8-бйТНЫЙ ТИП IMP
КМйгсная контрольная сумш ICMP
Содержание ICMP (в зависимости от типа и кода)
Рис. 13-1. Заголовок ICMP
В первом поле указан тип сообщения ICMP, которое может быть класси- фицировано или как запрос, или как ошибка. Поле кода определяет тип за- проса или сообщения. В поле контрольной суммы заключена 16-битная ком- плементарная сумма заголовка ICMP. Наконец, его содержание зависит от типа ICMP и кода (табл. 13-1)
Табл. 13-1. Типы сообщения ICMP
Тип Запрос или тип ошибки Код Описание
0 3
4 5
8 9
10
Запрос
Ошибка адресат недостижим
Ошибка
Ошибка:
перенаправление
Запрос
Запрос
Запрос
0 0
1 2
3 4
5 6
7 8
9 10 11 12 13 14 15 0
0 1
2 3
0 0
0
Эхо-ответ
Сеть недоступна
Узел недоступен
Протокол недоступен
Порт недоступен
Необходима фрагментация, но задан бит ее запрета
Ошибка на исходном маршруте
Сеть адресата неизвестна
Узел адресата неизвестен
Исходный узел изолирован (устаревшее)
Сеть адресата административно изолирована
Узел адресата административно изолирован
Сеть недоступна для TOS
Узел недоступен для TOS
Связь административно запрещена фильтрацией
Нарушение приоритета узлов ,i
Пренебрежение приоритетом узла . ftv<
Подавление отправителя >1П
Перенаправление для сети '
Перенаправление на узел
Перенаправление на TOS и сеть
Перенаправление на TOS и узел
Эхо-запрос г
Оповещение маршрутизатора
Запрос маршрутизатора

ГЛАВА 13 Простые сокеТы
401
Табл.
Тип
11 12 13 14 15 16 17 18
13-1. (продолжение)
Запрос или тип ошибки
Ошибка:
время превышено
Ошибка- проблема параметра
<¥* МП)
Запрос
Запрос
Запрос
Запрос "i <
Запрос ч-*
Запрос
Код
0 1
0 1 •*
0 0
0 0
0 0
Описание
TTL в ходе транзита равен 0
TTL в ходе повторной сборки р
Неверный заголовок IP
Отсутствует требуемый парам£
Запрос штампа времени г
Ответ штампа времени
Информационный запрос
Информационный ответ
Маска адреса запроса ,,
Маска адреса ответа
МНЦЙН
>ав««вмтомП.
\\
1
е
,иЧ *\
\\
180(1 \\
\\
Сгенерированное сообщение ICMP об ошибке всегда содержит заголовок
IP и первые 8 байт дейтаграммы IP, ставшей причиной ошибки. Это позво- ляет узлу, принявшему сообщение об ошибке, связать его с конкретным про- токолом и процессом. В нашем случае Ping полагается на эхо-запрос и эхо- ответ ICMP, а не на сообщение об ошибке. Применение ICMP в основном ог- раничено реакцией узлов на проблемы TCP или UDP В следующем разделе мы рассмотрим, как с помощью протокола ICMP с простым сокетом создать запрос Ping, используя эхо-запрос и сообщения эхо-ответа. vnt^qrj \\
Пример Ping 'aTebwjvt
Ping часто используют, чтобы определить, является ли конкретный узел дей- ствующим и доступным по сети Создав эхо-запрос ICMP и направив его на интересующий узел, можно определить, доступен ли этот узел. Конечно, до- ступность узла не гарантирует пользователю сокета возможность соединить- ся с процессом на этом узле (поскольку, например, процесс на удаленном сервере может быть не настроен для соединения). Но это означает, что се- тевой уровень удаленного узла реагирует на сетевые события. По сути, в этом примере Ping исполняет следующие шаги.
1. Создает сокет типа SOCKJIAW под протокол IPPROTOJCMP.
2. Создает и инициализирует заголовок ICMP. U
3. Вызывает sendto или WSASENDTO, чтобы отправить запрос ICMP на уда- ленный узел.
4. Вызывает recvfrom или WSARECVFROM для приема любых ICMP-откликов.
Когда вы отправляете эхо-запрос ICMP, удаленный компьютер прерыва- ет его и создает сообщение эхо-ответа Если по неким причинам узел не до- ступен, соответствующее ICMP-сообщение об ошибке (например, «узел ад- ресата не доступен») будет возвращено маршрутизатором где-нибудь на пути к месту назначения. Если физически сетевое соединение с узлом имеется, но удаленный узел временно не доступен или не реагирует на сетевые события,

402
ЧАСТЬ II Интерфейс прикладного программирования Wmsock вам придется задать время ожидания, чтобы определить это Листинг 13-1 на примере Ping с поясняет, как создать сокет для отправки и приема 1СМР-па- кетов, а также как использовать параметр сокета JPOPTIONS для реализации функции записи маршрута
J J b l
*
Листинг 13-1. Пример Ping
// Имя модуля Ping с Ы
II Параметры командной строки:
// Ping [host] [packet-size]
// host Строковое имя узла адресата
// packet-size Целый размер отправляемого пакета (меньше 1024 байт)
//«pragma pack(1)
«define WIN32_LEAN_AND_MEAN
«include
«include
«include
«include
«define IP_RECORD_ROUTE 0x7
// Структура заголовка IP
typedef struct _iphdr unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned
> IpHeader,
«define ICMP
ttdefme ICMP
int int char short short short char char short int int h_len 4;
version:4;
tos;
total_len;
ident,
frag_and_flags;
ttl;
proto;
checksum;
sourcelP;
destIP;
_ECHO 8
ECHOREPLY 0
//
//
//
//
//
//
//
//
//
Длина заголовка
Версия IP
Тип службы
Полный размер пакета
Уникальный идентификатор
Флаги
Срок жизни
Протокол (TCP, UDP и т.п.)
Контрольная сумма IP
,tq
«define ICMP_MIN
n
8 // Пакет ICMP не меньше 8 байт (заголовок)
// Структура заголовка ICMP
//
typedef struct _icmphdr

и, •., гплфА лл^пщшш шгщщ
«оз
bio/
Листинг 13-1. (продолжение) (штжшбоС^) t H nmtat,
{
BYTE i_type;
BYTE i_code; // Тип субкода
USHORT i_cksum;
USHORT i_id,
USHORT i_seq, 1
II Это нестандартный заголовок, но мы резервируем место для времени
ULONG timestamp,
} IcmpHeader, ,. it axe s\ v.
// Расширенный заголовок IP - применмяшшшфаметром
II сокета IP_OPTIONS • '„'$) ;0 « »W*J8J_I
typedef struct _ipoptionhdr '
{ / '
unsigned char code,
unsigned char : len; up unsigned char ptr;
unsigned long addr[9];
} IpOptionHeader;
«define DEF_PACKET_SIZE 32
«define MAX_PACKET 1024
«define MAX_IP_HDR_SIZE 60
BOOL bRecordRoute;
int datasize;
char «lpdest;
> etsb.«iaot
// Тип параметра
// Длина расширенного
// Смещение на параметры
// Перечень IP-адресов '
1
\\
it.
II
// Стандартный размер пакета \\
// Максимальный размер ICMP-пакета » .PNHXHV* \\
// Максимальный размер заголовка IP \\
// с параметрами ewr-wifl \\
шм «yt И т»влэ(» М \\
о »^9ф
¥
1
'чьаевху япд \\
\\
}
(«Ua tnt
t»t
// Функция: usage w,
II
II Описание:
// Выводит информацию об использовании
//
void usage(char *progname)
pnntf("usage
- ping -r
1   ...   29   30   31   32   33   34   35   36   ...   50


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