Перекрытый ввод-вывод существовал и в спецификации WinSock 1, но реализовывался только для линии NT. Специальных функций для перекрытого ввода-вывода в WinSock 1 не было, требовались функции ReadFile
и WriteFile
, в которые вместо дескриптора файла подставлялся дескриптор сокета. В WinSock 2 появилась полноценная поддержка перекрытого ввода-вывода для всех версий Windows, а в спецификацию добавились новые функции для его реализации, избавившие от необходимости использования функций файлового ввода-вывода. Здесь мы будем рассматривать перекрытый ввод-вывод только в спецификации WinSock 2, т. к. старый вариант из-за своих ограничений уже не имеет практического смысла.
Существуют два варианта уведомления о завершении операции перекрытого ввода-вывода: через событие и через процедуру завершения. Кроме того, программа может не дожидаться уведомления, а проверять состояние запроса перекрытого ввода-вывода с помощью функции WSAGetOverlappedResult
(ее мы рассмотрим позже).
Чтобы сокет мог использоваться в операциях перекрытого ввода-вывода, при его создании должен быть установлен флаг WSA_FLAG_OVERLAPPED
(функция socket
неявно устанавливает этот флаг). Для выполнения операций перекрытого ввода-вывода сокет не нужно переводить в какой-либо особый режим, достаточно обычные функции send
и recv
заменить на WSARecv
и WSASend
. Сначала мы рассмотрим функцию WSARecv
, прототип которой приведен в листинге 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).
TWSAOverlapped