Читаем О чём не пишут в книгах по Delphi полностью

Перекрытый ввод-вывод существовал и в спецификации WinSock 1, но реализовывался только для линии NT. Специальных функций для перекрытого ввода-вывода в WinSock 1 не было, требовались функции ReadFile и WriteFile, в которые вместо дескриптора файла подставлялся дескриптор сокета. В WinSock 2 появилась полноценная поддержка перекрытого ввода-вывода для всех версий Windows, а в спецификацию добавились новые функции для его реализации, избавившие от необходимости использования функций файлового ввода-вывода. Здесь мы будем рассматривать перекрытый ввод-вывод только в спецификации WinSock 2, т.к. старый вариант из-за своих ограничений уже не имеет практического смысла.

Существуют два варианта уведомления о завершении операции перекрытого ввода-вывода: через событие и через процедуру завершения. Кроме того, программа может не дожидаться уведомления, а проверять состояние запроса перекрытого ввода-вывода с помощью функции WSAGetOverlappedResult (ее мы рассмотрим позже).

Чтобы сокет мог использоваться в операциях перекрытого ввода-вывода, при его создании должен быть установлен флаг WSA_FLAG_OVERLAPPED (функция socket неявно устанавливает этот флаг). Для выполнения операций перекрытого ввода-вывода сокет не нужно переводить в какой-либо особый режим, достаточно обычные функции send и recv заменить на WSARecv и WSASend. Сначала мы рассмотрим функцию WSARecv, прототип которой приведен в листинге 2.68.

Листинг 2.68. Функция WSARecv

// ***** Описание на C++ *****

int WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

// ***** Описание на Delphi *****

function WSARecv(S: TSocket; lpBuffers: PWSABuf; dwBufferCount: DWORD; var NumberOfBytesRecvd: DWORD; var Flags: DWORD; lpOverlapped: PWSAOverlapped; lpCompletionRoutine: TWSAOverlappedCompletionRoutine): Integer;

Перекрытым вводом-выводом управляют два последних параметра функции, но WSARecv обладает и другими дополнительными по сравнению с функцией recv возможностями, не связанными с перекрытым вводом-выводом. Если оба этих параметра равны nil, или сокет создан без указания флага WSA_FLAG_OVERLAPPED, функция работает в обычном блокирующем или неблокирующем режиме, который установлен для сокета. При этом ее поведение отличается от поведения функции recv только тремя незначительными аспектами: во-первых, вместо одного буфера ей можно передать несколько, заполняемых последовательно. Во-вторых, флаги передаются ей не как значение, а как параметр-переменная, и при некоторых условиях функция WSARecv может их изменять (при использовании TCP и UDP флаги никогда не меняются, поэтому мы не будем рассматривать здесь эту возможность). В-третьих, при успешном завершении функция WSARecv возвращает ноль, а не число прочитанных байтов (последнее возвращается через параметр lpNumberOfBytesRecvd).

Буферы, в которые нужно поместить данные, передаются функции WSARecv через параметр lpBuffers. Он содержит указатель на начало массива структур TWSABuf, а параметр dwBufferCount — число элементов в этом массиве. Ранее мы знакомились со структурой TWSABuf (см. листинг 2.39): она содержит указатель на начало буфера и его размер. Соответственно, массив таких структур определяет набор буферов. При чтении данных заполнение буферов начинается с первого буфера в массиве lpBuffers, затем, если в нем не хватает места, заполняется второй буфер и т.д. Функция не переходит к следующему буферу, пока не заполнит предыдущий до последнего байта. Таким образом, данные, получаемые с помощью функции WSARecv, могут быть помещены в несколько несвязных областей памяти, что иногда бывает удобно, если принимаемые сообщения имеют строго определенный формат с фиксированными размерами компонентов пакета: в этом случае можно каждый компонент поместить в свой независимый буфер.

Теперь переходим непосредственно к рассмотрению перекрытого ввода-вывода на основе событий. Для реализации этого режима при вызове функции WSARecv параметр lpCompletionRoutine должен быть равен nil, а через параметр lpOverlapped передается указатель на запись TWSAOverlapped, которая определена следующим образом (листинг 2.69).

Листинг 2 69. Тип TWSAOverlapped

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже