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

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


Скачать 2.88 Mb.
НазваниеЭ. Джонс, Д. Оланд
АнкорПрограммирование в сетях Windows.pdf
Дата12.10.2017
Размер2.88 Mb.
Формат файлаpdf
Имя файлаПрограммирование в сетях Windows.pdf
ТипКнига
#9346
страница21 из 50
1   ...   17   18   19   20   21   22   23   24   ...   50

// если не удалось перевести сокет в неблокирующий режим
}
Если сокет находится в неблокирующем режиме, функции Winsock завер- шаются немедленно В большинстве случаев они будут возвращать ошибку
I

Г Л А В А 8 Ввод вывод в Winsock 209
WSAEWOULDBLOCK, означающую, что требуемая операция не успела завер- шиться за время вызова Например, вызов recv вернет WSAEWOULDBLOCK,
если ч системном буфере нет данных Часто функцию требуется вызывать несколько раз подряд, пока не будет возвращен код успешного завершения
Вот список возвращаемых значений WSAEWOULDBLOCK при вызове наибо- лее часто встречающихся функций Winsock
щ WSAAccept и accept — приложение не получило запрос на соединение,
повторите вызов для проверки наличия запросов,
щ closesocket скорее всего, функция setsockopt была вызвана с парамет- ром SOJJNGER и задан ненулевой тайм-аут,
WSAConnect и connect — соединение инициировано, для проверки за- вершения повторите вызов,
щ WSARecv, recv, WSARecvFrom и recvfrom — данные не были приняты,
повторите проверку позже,
Ш WSASend, send, WSASendTo и sendto в буфере нет места для записи исходящих данных, попробуйте вызвать функцию позже
Так как большинство вызовов в неблокирующем режиме будут возвращать ошибку WSAEWOULDBLOCK, анализируйте все возвращаемые коды ошибок и будут готовы прервать операцию в любой момент К сожалению, многие про- граммисты непрерывно вызывают функцию до успешного завершения Меж- ду тем, для чтения 200 байт данных создание цикла, состоящего лишь из вызова recv, ничуть не лучше, чем вызов в блокирующем режиме с флагом
MSG_PEEK, обсуждавшийся ранее Модели ввода-вывода Winsock могут по- мочь приложению определить, когда сокет доступен для чтения и записи
У каждого режима работы сокета свои достоинства и недостатки Режим блокировки проще концептуально, но в нем сложнее обрабатывать несколь- ко сокетов одновременно или нерегулярные потоки данных С другой сто- роны, режим без блокировки требует больше кода для обработки ошибки
WSAEWOULDBLOCK в каждом вызове Модели ввода-вывода сокетов помога- ют приложению асинхронно обрабатывать соединения на одном и более сокетах
Модели ввода-вывода сокетов
Приложения Winsock могут использовать пять моделей для управления вво-
Дом-выводом select, WSAAsyncSelect, WSAEventSelect, перекрытый ввод-вывод и порты завершения В этом разделе объясняются особенности каждой моде- ли и основные принципы управления сокетами На прилагаемом компакт- диске приведены примеры приложений, показывающие разработку простей- е г о
TCP-сервера с учетом принципиальных особенностей каждой модели
Модель select
а модель наиболее широко доступна в Winsock Мы называем ее select, по-
У что управление вводом-выводом в ней основано на использовании
Ункц select Идея восходит к модели сокетов Беркли для Unix Эта модель

2 1 0 ЧАСТЬ II Интерфейс прикладного программирования Winsock была встроена в Winsock 1.1, чтобы дать приложениям, избегающим блоки- ровки при вызове сокета, возможность управлять несколькими сокетами в определенном порядке. Так как Winsock версии 1.1 совместим с Беркли приложение, использующее сокеты Беркли и функцию select, в принципе может выполняться в Windows без модификации.
Функцию select используют и для определения, есть ли в сокете данные и можно ли туда записать новые. Основная цель функции — избежать бло- кировки приложения при связанных с вводом-выводом вызовах, например
send или recv, когда сокет работает в блокирующем режиме, и предотвратить появление ошибки WSAEWOULDBLOCK — в неблокирующем. Функция select
блокирует операции ввода-вывода, пока не будут соблюдены условия, задан- ные в качестве параметров. Прототип функции select:
int н
select(
int fd_
fd_
fd nfds,
set FAR
set FAR
set FAR
* readfds,
* writefds,
• exceptfds,
const struct timeval FAR * timeout
);
Первый параметр — nfds, игнорируется, он включен лишь для совмести- мости с приложениями Беркли. Заметьте, что есть три параметра с типом
fdset: один для проверки возможности чтения — readfds, другой для провер- ки возможности записи — writefds и третий для срочных (out of band, OOB)
данных — exceptfds. Тип fdset представляет набор сокетов. Набор readfds
определяет сокеты, удовлетворяющие одному из следующих условий:
данные доступны для чтения;
Ш соединение закрыто, сброшено или завершено;
• если вызвать функцию listen, когда соединение находится в состоянии ожидания, вызов функции accept будет успешным.
Набор writefds определяет сокеты, удовлетворяющие одному из следую- щих условий-
Ш возможна отправка данных;
• если обрабатывается неблокирующий вызов соединения, попытка соеди- нения удалась.
Наконец, набор exceptfds определяет сокеты, удовлетворяющие одному из следующих условий:
• если обрабатывается неблокирующий вызов соединения, попытка соеди- нения не удалась;
Ш ООВ-данные доступны для чтения.
Например, чтобы проверить возможность чтения из сокета, добавьте ег в набор readfds и подождите завершения функции select. Затем проверьте,
входит ли еще этот сокет в набор readfds. Если да, то сокет доступен для чте ния и можно работать с его данными. Любые два из трех параметров

Г Л А В А 8 Ввод-вывод в Wmsock 211
fSy exceptfds) могут быть NULL (но хотя бы один должен быть не NULL).
Любой ненулевой набор должен содержать хотя бы один описатель сокета,
иначе функции select будет нечего ожидать. Последний параметр — timeout,
представляет собой указатель на структуру timeval, определяющую, сколько времени select будет ждать окончания ввода-вывода. Если timeout равен NULL,
select будет ждать, пока не найдет хотя бы один описатель, отвечающий за- данному критерию. Структура timeval определена так:
struct timeval
{
long tv_seo;
long tv_usec;
};
Поле tv_sec задает время ожидания в секундах, а поле tv_usec — в милли- секундах. Тайм-аут {0, 0} означает, что функция select должна завершаться немедленно, позволяя приложению определить ее результат. Впрочем, это- го следует избегать. При успешном завершении select возвращает в структу-
paxfd_set общее количество описателей сокетов, у которых есть ожидающие операции ввода-вывода. Если время timeval истекает, возвращается 0. В слу- чае любой неудачи select возвращает SOCKETJ1RROR.
Перед тем как отслеживать сокеты с помощью select, ваше приложение должно сформировать одну или все структуры fd_set, присвоив набору опи- сатели. Добавив сокет в один из наборов, вы сможете узнать, происходила ли конкретная операция ввода-вывода с этим сокетом. В Winsock определе- ны следующие макросы для работы с наборамиу#_$е£
FD_CLR(s, *set) удаляет сокет 5 из набора set;
Ш FD_ISSET(s, *set) проверяет, входит ли сокет s в набор set;
li FD_SET(s, *set) — добавляет сокет s в набор set;
Ш FDZERO(*set) инициализирует set как пустой набор.
Например, если вы хотите узнать, можно ли читать данные из сокета без блокировки, добавьте его в набор fdjread при помощи макроса FD_SET и вызовите select. Чтобы проверить, остался ли сокет в этом наборе, исполь- зуйте макрос FDJSSET. Вот типичный алгоритм применения select для рабо- ты с одним или несколькими сокетами.
1 • Инициализируйте все интересующие вас fdset макросом FDZERO.
Добавьте описатели сокетов в соответствующие наборы fdjset макросом
Вызовите функцию select и дождитесь результата: select вернет общее ко- личество описателей, оставшихся в наборах, и соответственно обновит сами наборы.
спользуя результат работы select, приложение может определить, какие сокеты осуществляют ввод-вывод в данное время, проверяя каждый fd_set
макросом FD ISSET.

,
n 11. Ч А С I ь II Интерфейс прикладного программирования Winsock
____
5 Выявив активные сокеты, обработайте их ввод-вывод и продолжите с шага 1
По завершении работы функция select удаляет из каждой структуры/rf set
описатели сокетов, не участвующих в операциях ввода-вывода Этим объяс- няется необходимость использовать макросы FDISSETm шаге 4, чтобы оп- ределить, является ли конкретный сокет частью набора В листинге 8-4 по- казаны основные этапы реализации модели select для одного сокета Для не- скольких сокетов нужно обработать список или массив дополнительных со- кетов
Листинг 8-4. Применение модели select для управления вводом-выводом
через сокет
SOCKET s,
fd_set fdread,
mt ret,
// Создание сокета и установление соединения
// Управление вводом-выводом сокета while(TRUE)
{
// Всегда очищайте набор перед вызовом
// selectO
FD_ZERO(&fdread),
i
// Добавление сокета s к набору для проверки чтения
с
* if ((ret = select(0, &fdread, NULL, NULL, NULL))
== SOCKET ERROR)
& /I Обработка ошибки
с
}
h
if (ret > 0)
n
{
Ш // В этом простейшем случае selectO должна вернуть 1
щ /I Приложение, работающее с несколькими сокетами,
// может получить большую величину
// Здесь должна быть проверка,
// входит ли сокет в набор
if (FD_ISSET(s, &fdread))
{
// Через сокет s идет чтение
}

Г Л А В А 8 Ввод вывод в Winsock 213
Модель WSAAsyncSelect
Winsock поддерживает полезную асинхронную модель ввода-вывода, позво- ляющую приложению получать информацию о событиях, связанных с со- кетом, при помощи сообщений Windows Это достигается вызовом функции
WSAAsyncSelect после создания сокета Данная модель первоначально появи- лась в приложениях Winsock 1 1 для облегчения взаимодействия приложе- ний в многозадачной среде 16-битных платформ, таких как Windows for
Workgroups Но она полезна и для современных приложений, особенно если они обрабатывают сообщения окон в стандартной процедуре {ivmproc) Эта модель также используется объектом CSocket из библиотеки классов Microsoft
(Microsoft Foundation Class, MFC)
Уведомления о сообщениях
Прежде чем использовать модель WSAAsyncSelect, приложение должно со- здать окно, используя функцию CreateWmdow, и процедуру обработки сооб- щений (ivmproc) для этого окна Можно использовать диалоговое окно с диалоговой процедурой (так как это частный случай окна) Здесь достаточ- но продемонстрировать простое окно с дополнительной процедурой Со- здав инфраструктуру окна, вы вправе создавать сокеты и активизировать уведомления вызовом функции WSAAsyncSelect
int WSAAsyncSelect(
SOCKET s,
HWND hWnd,
unsigned int wMsg,
long lEvent
Параметр s — интересующий нас сокет Параметр hWnd — описатель окна
(или диалога), которое должно получить уведомление, когда произойдет сетевое событие Параметр wMsg определяет сообщение, которое будет в этом случае отправлено окну с описателем hWnd Обычно сообщению при- сваивается код выше WMJJSER, чтобы избежать совпадения сетевых сообще- ний со стандартными сообщениями окна Последний параметр — lEvent, за- дает битовую маску, определяющую комбинацию сетевых событий, которые нужно отслеживать Эти события принимают уведомления о к
FDREAD — готовности к чтению, *
FDJWRITE готовности к записи,
FDOOB — получении срочных данных, »
| Я 1
FO_ACCEPT — входящих соединениях,
ж
FDCONNECT завершении соединения или многоточечной операции
join,
F
^CLOSE — закрытии сокета,
— изменении QoS,
прикладного программирования winsocK
Я FDGROUPQOS — изменении QoS (зарезервировано для будущего ис- пользования группами сокетов),
FD ROUTING INTERFACECHANGE изменении интерфейса марщру.
тизации для указанных адресов,
FDADDRESSIISTCHANGE — изменении списка локальных адресов для семейства протокола сокета
В большинстве случаев нужно отслеживать события типов FD READ, FD
WRITE, FD_ACCEPT, FDJCONNECTYL FDJOLOSE Конечно, обработка событий
FD_ACCEPT пли FDjOONNECT зависит от того, является приложение клиен- том или сервером Если приложению нужно отслеживать несколько типов событий, присвойте параметру lEvent значение, полученное побитовым ИЛИ
над масок соответствующих типов
WSAAsyncSelect(s, hwnd, WM_S0CKET,
FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE);
Тогда приложение получит уведомления о событиях установления соеди- нения, приема, передачи и закрытия сокета При этом невозможно зарегис- трировать несколько событий, происходящих на сокете одновременно. Уве- домления о событиях на сокете остаются включенными, пока сокет не за- крыт функцией closesocket или не изменен набор регистрируемых сетевых событий повторным вызовом WSAAsyncSelect для того же сокета. Присвоив О
параметру lEvent, вы прекратите отправку всех уведомлений о событиях на сокете
При вызове функции WSAAsyncSelect сокет автоматически переходит в неблокирующий режим В результате такие функции Winsock, как WSARecv,
при вызове возвратят ошибку WSAEWOULDBLOCK, если в буфере нет данных
Чтобы избежать ошибки, приложение должно опираться на пользовательс- кое оконное сообщение, заданное в параметре wMsg при вызове WSAAsync-
Select, и показывающее, когда на сокете происходят сетевые события того или иного типа.
После успешного вызова WSAAsyncSelect приложение будет получать уве- домления о событиях на сокете в виде сообщений Windows, отправляемых окну из параметра hWnd Получающая эти сообщения процедура окна опре- делена так
LRESULT CALLBACK WindowProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam, '"
LPARAM lParam
Здесь параметр hWnd — описатель окна, вызвавшего оконную процеДУРУ
Параметр uMsg обозначает сообщение, которое нужно обработать. В данном случае мы будем перехватывать сообщение, определенное в вызове WSAAsy
nc


Select. Параметр wParam определяет сокет, на котором произошло сетевое событие. Он необходим, если к одной оконной процедуре привязано несколь

Г Л А В А 8 Ввод-вывод в Winsock 215
сокетов Параметр IParam состоит из двух частей младшее слово указыва- произошедшее событие, а старшее — содержит код ошибки.
Когда процедура окна получает сообщение о сетевом событии, она в пер- вую очередь проверяет старшее слово параметра IParam, чтобы определить,
не было ли ошибки Существует специальный макрос — WSAGETSELECTERROR,
возвращающий код ошибки в старшем слове После этого нужно определить тип события, инициировавшего сообщение, а для этого — прочитать младшее слово IParam Значение этого слова возвращает макрос WSAGETSELECTEVENT
В листинге 8-5 показана обработка сообщения окна при использовании модели WSAAsyncSelect Выделены последовательные шаги, необходимые для разработки любого сервера и опущены фрагменты, требующиеся для пол- ноценной функциональности в среде Windows.
Листинг 8-5. Программирование сервера в модели WSAAsyncSelect
«define WM_S0CKET WM_USER + 1
«include
int WINAPI WinMain(HINSTANCE hlnstance,
HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{
SOCKET Listen;
HWND Window;
// Создание окна и привязка процедуры ServerWinProc
Window = CreateWmdowO;
// Запуск Winsock и создание сокета
WSAStartup(...);
Listen = SocketO;
// Привязка сокета к порту 5150 и прослушивание соединений
InternetAddr.sin_family = AF_INET;
InternetAddr.sm_addr.s_addr = htonl(INADDR_ANY);
InternetAddr.sin_port = htons(5150);
bind(Listen, (PSOCKADDR) ilnternetAddr,
n
sizeof(InternetAddr)); "„..
i 01
// Настройка уведомлений с сокета,
// используя сообщение WM_S0CKET, определенное выше
WSAAsyncSelect(Listen, Window, WM_S0CKET,
FD.ACCEPT | FD_CLOSE);
listenUisten, 5);
:ЭТ1Я1ЦН «sea
см. след. стр.

Листинг 8-5. {продолжение)
I/ Трансляция и обработка сообщения окна до окончания работы приложения
BOOL CALLBACK ServerWinProc(HWND hDlg,WORD wMsg,
WORD wParam, DWORD lParam)
{
SOCKET Accept;
switch(wMsg)
{
case WM_PAINT:
// Обработка сообщений прорисовки окна break;
case WM_SOCKET:
// Определение возможных ошибок
// макросом WSAGETSELECTERRORO
if (WSAGETSELECTERROR(lParam))
{
// Вывод сообщения об ошибке и закрытие сокета closesocket(wParam);
break;
// Определение типа произошедшего события switch(WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT:
// Прием входящего соединения
Accept = accept(wParam, NULL, NULL);
// Подготовка сокета принятого соединения для отправки
// уведомлений о чтении, записи и закрытии "Ч'.м WSAAsyncSelect(Accept, hwnd, WM_SOCKET,
•У 1 FD.READ | FD_WRITE | FD_CLOSE);
к »Hi break;
4
< W
, case FD_READ:
// Прием данные из сокета в wParam break;
case FD_WRITE:

Г Л А В А 8 Ввод-вывод в Winsock 217
1   ...   17   18   19   20   21   22   23   24   ...   50


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