Программирование в сетях Windows
.pdf388 |
ЧАСТЬ II |
Интерфейс прикладного программирования Winsock |
|||||
|
|
|
|
|
|
|
|
Листинг 12-1. |
(продолжение) |
|
|
|
|||
|
|
printf("FD_WRITEn"); |
ШОА.С |
|
|
||
|
if (ne.lNetworkEvents & FtyMD ' |
|
|
||||
|
|
pnntf("FD_QOS\n"); |
|
|
|
||
|
return; |
|
|
|
|
|
|
// Функция: Client |
|
st |
|
|
|
||
|
|
|
|
|
|||
// Описание: |
|
|
|
|
|
|
|
// |
Подпрограмма |
клиента инициирует |
соединение, |
задает QOf в нужны! момент |
|||
// |
времени и |
обрабатывает |
входящие |
события. |
f'Ч |
||
|
|
|
|
|
|
|
|
void |
Client(SOCKET |
s) |
|
|
|
|
|
{ |
|
server, |
|
|
|
|
|
|
SOCKADDR_IN |
|
|
|
|
||
|
|
local; |
;()tfievls |
|
|
|
|
|
WSABUF |
wbuf; |
|
|
|
|
|
|
DWORD |
dwBytes, |
|
|
|
|
|
|
|
dwBytesSent, |
|
|
|
|
|
|
|
dwBytesRecv, |
|
|
|
|
|
|
|
dwFlags; |
|
|
|
|
|
|
HANDLE |
hEvent; |
|
|
. |
,i- |
|
|
int |
ret, i; |
|
,j,. |
|
||
|
char |
databuf[DATA_BUFFER_SZ]; |
ТЗЖ» |
ti |
|||
|
QOS |
*lpqos; |
|
|
|
|
|
|
WSANETWORKEVENTS ne; |
|
|
Ш о о 1 А Г |
|||
|
hEvent = WSACreateEvent(); |
|
|
Him, |
|||
|
|
|
|
if (hEvent === NULL)
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); |
ve |
local.sin_addr.s_addr = htonl(INADDR_ANY); |
|
t Tt
if (bind(s, (SOCKADDR *)&local, sizeof(local)) == SOCKET_ERROR)
printf("bind() failed: Xd\n", WSAGetLastErrorO); return;
390 ЧАСТЬ 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?u3 |
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 |
»* Зет |
||
|
ret = WSARecv(s, &wbuf, 1, |
idwBytesRecv, |
&dwFlags, |
|
'> |
|
|||||
|
|
NULL, |
NULL); |
|
|
|
|
|
4 |
|
|
|
if |
(ret |
== |
SOCKET.ERROR) |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
< |
< |
|
|
|
|
printf("WSAReov() failed: Xd\n", |
|
|
|
|
|
|
|||
|
|
|
WSAGetLastErrorO); |
|
|
|
|
|
|
||
|
|
return; |
|
|
|
|
|
|
^Ш |
||
|
) |
|
|
|
|
|
|
|
Л |
|
|
|
printf("Read: Xd bytes\n", |
dwBytesRecv); |
|
|
<i) |
tl |
|||||
|
wbuf.len |
= |
dwBytesRecv; |
|
|
|
|
|
|
|
|
|
ret = WSASend(s, &wbuf, 1, |
&dwBytesSent, |
0, |
NULL, |
|
|
|
392 ЧАСТЬ 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 |
buf[QOS_BUFFER_SZ]; |
|
|
|
|
||||
QOS |
•lpqos = NULL; |
|
|
|
|
|
|||
DWORD |
dwBytes; |
|
|
|
|
|
|
||
BOOL |
bRecvRESV = |
FALSE; |
|
|
|
|
|||
if |
(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; |
A |
} |
|
printfC'Sent: Xd bytes\n", |
dwBytesSent); |
>
return;
//Функция: main
//Описание:
//Инициализирует Winsock, анализирует аргументы командой строки, создает
//сокет QOS TCP и вызывает соответствующую подпрограмму обработки
//в зависимости от переданных аргументов
//
int main(int argc, char **argv)
{
WSADATA |
wsd; |
см.след.стр. |
|
|
394 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Листинг 12-1. |
(продолжение) |
|
|
|
„ |
Л- |
|
|||
WSAPR0T0C0L_INF0 *pinfo = NULL; |
|
|
|
• ,jf |
|
|
||||
SOCKET |
|
|
s; |
|
|
|
|
?H00# » |
ti |
|
|
|
|
|
|
|
|
|
|
} |
|
// Анализ командной |
строки |
|
|
. Ц5У |
|
|
||||
ValidateArgs(argc, |
argv); |
|
|
,Jj 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; |
|
|
|
|
|
|
'• <j |
|
} |
|
|
|
|
|
|
|
|
|
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 |
|
|
> |
|
|
|
|
|
лот |
Aft |
|
|
|
InitQosO; |
|
|
|
|
|
|
|
|
|
|
if |
(bServer) |
|
|
|
|
|
|
|
|
|
|
Server(s); |
|
|
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
|
|
|
|
Client(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 вызывается с объектом QOSJDESTADDR, но над флагом SERVICE_QOS_NOJSIGNALING и значением поля ServiceType структуры 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. В примере с многоадресной рассылкой использованы те же параметры, что и в примере с одноад-
396 |
ЧАСТЬ 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 из Winsock QoS изначально доступна в сети ATM, так что собственные сеть, приложение и компоненты политики, необходимые для QoS через IP, не требуются Это же относится и к службе управления доступом (Admission Control Service, ACL) и к протоколу RSVP Вместо этого, коммутатор ATM выделяет пропускную способность и предотвращает ее избыточное использование
Помимо этих различий функции Winsock API работают с QoS в сетях ATM немного иначе, чем с QoS поверх IP Первое главное отличие — запрос пропускной способности QoS обрабатывается, как часть запроса соединения Это отличается от QoS поверх IP — сеанс RSVP устанавливается отдельно от соединения Если запрос пропускной способности отклонен в ATM, соеди-