Поделиться через


Функция AcceptEx (winsock.h)

Функция AcceptEx принимает новое подключение, возвращает локальный и удаленный адрес и получает первый блок данных, отправленный клиентским приложением.

Примечание Эта функция является расширением майкрософт для спецификации Сокетов Windows.

 

Синтаксис

BOOL AcceptEx(
  [in]  SOCKET       sListenSocket,
  [in]  SOCKET       sAcceptSocket,
  [in]  PVOID        lpOutputBuffer,
  [in]  DWORD        dwReceiveDataLength,
  [in]  DWORD        dwLocalAddressLength,
  [in]  DWORD        dwRemoteAddressLength,
  [out] LPDWORD      lpdwBytesReceived,
  [in]  LPOVERLAPPED lpOverlapped
);

Параметры

[in] sListenSocket

Дескриптор, определяющий сокет, который уже был вызван с помощью функции прослушивания . Серверное приложение ожидает попыток подключения к этому сокету.

[in] sAcceptSocket

Дескриптор, определяющий сокет, в котором принимается входящее подключение. Этот сокет не должен быть привязан или подключен.

[in] lpOutputBuffer

Указатель на буфер, который получает первый блок данных, отправленных по новому подключению, локальный адрес сервера и удаленный адрес клиента. Полученные данные записываются в первую часть буфера, начиная с нуля смещения, а адреса записываются во вторую часть буфера. Этот параметр должен быть указан.

[in] dwReceiveDataLength

Количество байтов в lpOutputBuffer , которые будут использоваться для фактических данных получения в начале буфера. Этот размер не должен включать размер локального адреса сервера и удаленного адреса клиента; они добавляются в выходной буфер. Если параметр dwReceiveDataLength равен нулю, прием подключения не приведет к операции получения. Вместо этого AcceptEx завершается сразу после поступления подключения, не дожидаясь каких-либо данных.

[in] dwLocalAddressLength

Число байтов, зарезервированных для сведений о локальном адресе. Это значение должно быть не менее чем на 16 байт больше, чем максимальная длина адреса для используемого транспортного протокола.

[in] dwRemoteAddressLength

Число байтов, зарезервированных для сведений об удаленном адресе. Это значение должно быть не менее чем на 16 байт больше, чем максимальная длина адреса для используемого транспортного протокола. Не может быть равен нулю.

[out] lpdwBytesReceived

Указатель на DWORD , получающий количество полученных байтов. Этот параметр задается только в том случае, если операция завершается синхронно. Если он возвращает ERROR_IO_PENDING и завершается позже, то этот параметр DWORD никогда не устанавливается и необходимо получить число байтов, прочитанных из механизма уведомления о завершении.

[in] lpOverlapped

Структура OVERLAPPED , используемая для обработки запроса. Этот параметр должен быть указан; значение не может иметь значение NULL.

Возвращаемое значение

Если ошибка не возникает, функция AcceptEx успешно завершена и возвращается значение TRUE .

Если функция завершается сбоем, AcceptEx возвращает значение FALSE. Затем можно вызвать функцию WSAGetLastError для возврата расширенных сведений об ошибке. Если WSAGetLastError возвращает ERROR_IO_PENDING, операция была успешно инициирована и все еще выполняется. Если ошибка WSAECONNRESET, входящее подключение было указано, но впоследствии было прервано удаленным одноранговым узелом перед принятием вызова.

Комментарии

Функция AcceptEx объединяет несколько функций сокета в одном переходе МЕЖДУ API и ядром. Функция AcceptEx при успешном выполнении выполняет три задачи:

  • Принимается новое подключение.
  • Возвращаются как локальные, так и удаленные адреса для подключения.
  • Получается первый блок данных, отправляемый удаленным.
Примечание Указатель функции для функции AcceptEx должен быть получен во время выполнения путем вызова функции WSAIoctl с указанным SIO_GET_EXTENSION_FUNCTION_POINTER кодом операции. Входной буфер, передаваемый в функцию WSAIoctl , должен содержать WSAID_ACCEPTEX, глобальный уникальный идентификатор (GUID), значение которого определяет функцию расширения AcceptEx . При успешном выполнении выходные данные, возвращаемые функцией WSAIoctl , содержат указатель на функцию AcceptEx . GUID WSAID_ACCEPTEX определяется в файле заголовка Mswsock.h .
 

Программа может быстрее подключиться к сокету с помощью функции AcceptEx вместо функции accept .

Один выходной буфер получает данные, адрес локального сокета (сервер) и адрес удаленного сокета (клиент).

Использование одного буфера повышает производительность. При использовании AcceptEx необходимо вызвать функцию GetAcceptExSockaddrs для анализа буфера на три отдельные части (данные, адрес локального сокета и адрес удаленного сокета). В Windows XP и более поздних версий после завершения функции AcceptEx и установки параметра SO_UPDATE_ACCEPT_CONTEXT в принятом сокете локальный адрес, связанный с принятым сокетом, также можно получить с помощью функции getsockname . Аналогичным образом удаленный адрес, связанный с принятым сокетом, можно получить с помощью функции getpeername .

Размер буфера для локального и удаленного адресов должен быть на 16 байт больше размера структуры sockaddr для используемого транспортного протокола, так как адреса записываются во внутреннем формате. Например, размер sockaddr_in (структура адресов для TCP/IP) составляет 16 байт. Поэтому для локальных и удаленных адресов необходимо указать размер буфера не менее 32 байт.

Функция AcceptEx использует перекрывающиеся ввод-вывод, в отличие от функции accept . Если приложение использует AcceptEx, оно может обслуживать большое количество клиентов с относительно небольшим количеством потоков. Как и во всех перекрывающихся функциях Windows, в качестве механизма уведомления о завершении можно использовать события Windows или порты завершения.

Еще одно ключевое различие между функцией AcceptEx и функцией accept заключается в том, что Для acceptEx требуется, чтобы у вызывающего объекта уже было два сокета:

  • Один из них, указывающий сокет, в котором будет выполняться прослушивание.
  • Один из них, указывающий сокет, в котором будет приниматься подключение.

Параметр sAcceptSocket должен быть открытым сокетом, который не является ни привязанным, ни подключенным.

Параметр lpNumberOfBytesTransferred функции GetQueuedCompletionStatus или GetOverlappedResult указывает количество байтов, полученных в запросе.

После успешного завершения этой операции можно передать sAcceptSocket , но только для следующих функций:

ReadFile
WriteFile
send
WSASend
Recv
WSARecv
TransmitFile
closesocket
setsockopt(только для SO_UPDATE_ACCEPT_CONTEXT)
Примечание Если функция TransmitFile вызывается с флагами TF_DISCONNECT и TF_REUSE_SOCKET, указанный сокет возвращается в состояние, в котором он не привязан и не подключен. Затем дескриптор сокета можно передать в функцию AcceptEx в параметре sAcceptSocket , но сокет нельзя передать в функцию ConnectEx .
 

Когда функция AcceptEx возвращает значение , сокет sAcceptSocket находится в состоянии по умолчанию для подключенного сокета. Сокет sAcceptSocket не наследует свойства сокета, связанного с параметром sListenSocket , пока в сокете не будет задано SO_UPDATE_ACCEPT_CONTEXT. Используйте функцию setsockopt , чтобы задать параметр SO_UPDATE_ACCEPT_CONTEXT, указав sAcceptSocket в качестве дескриптора сокета и sListenSocket в качестве значения параметра.

Пример:

//Need to #include <mswsock.h> for SO_UPDATE_ACCEPT_CONTEXT

int iResult = 0;

iResult =  setsockopt( sAcceptSocket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, 
    (char *)&sListenSocket, sizeof(sListenSocket) );
   

Если предоставлен буфер приема, перекрываемая операция не будет завершена до тех пор, пока не будет принято подключение и данные не будут считаны. Используйте функцию getsockopt с параметром SO_CONNECT_TIME, чтобы проверка, было ли принято соединение. Если он был принят, вы можете определить, как долго установлено подключение. Возвращаемое значение — это количество секунд, в течение которых сокет был подключен. Если сокет не подключен, метод getsockopt возвращает 0xFFFFFFFF. Приложения, которые проверка, завершена ли перекрывающаяся операция, в сочетании с параметром SO_CONNECT_TIME, могут определить, что подключение было принято, но данные не получены. Тщательное изучение соединения таким образом позволяет приложению определить, не получили ли установленные в течение некоторого времени соединения данные. Рекомендуется прерывать такие подключения, закрывая принятый сокет, что приводит к ошибке при вызове функции AcceptEx .

Пример:


INT seconds;
INT bytes = sizeof(seconds);
int iResult = 0;

iResult = getsockopt( sAcceptSocket, SOL_SOCKET, SO_CONNECT_TIME,
                      (char *)&seconds, (PINT)&bytes );

if ( iResult != NO_ERROR ) {
    printf( "getsockopt(SO_CONNECT_TIME) failed: %u\n", WSAGetLastError( ) );
    exit(1);
}

Примечание Все операции ввода-вывода, инициированные данным потоком, отменяются при выходе из этого потока. Для перекрывающихся сокетов ожидающие асинхронные операции могут завершиться сбоем, если поток закрывается до завершения операций. Дополнительные сведения см. в разделе ExitThread .
 

Windows Phone 8. Эта функция поддерживается для приложений Магазина Windows Phone Windows Phone 8 и более поздних версий.

Windows 8.1 и Windows Server 2012 R2. Эта функция поддерживается для приложений Магазина Windows в Windows 8.1, Windows Server 2012 R2 и более поздних версий.

Пример кода

В следующем примере используется функция AcceptEx , использующая перекрывающиеся порты ввода-вывода и завершения.
#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#include <stdio.h>

// Need to link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

int main()
{
    //----------------------------------------
    // Declare and initialize variables
    WSADATA wsaData;
    int iResult = 0;
    BOOL bRetVal = FALSE;

    HANDLE hCompPort;
    HANDLE hCompPort2;
    
    LPFN_ACCEPTEX lpfnAcceptEx = NULL;
    GUID GuidAcceptEx = WSAID_ACCEPTEX;
    WSAOVERLAPPED olOverlap;

    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET AcceptSocket = INVALID_SOCKET;
    sockaddr_in service;
    char lpOutputBuf[1024];
    int outBufLen = 1024;
    DWORD dwBytes;

    hostent *thisHost;
    char *ip;
    u_short port;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"Error at WSAStartup\n");
        return 1;
    }    

    // Create a handle for the completion port
    hCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (u_long) 0, 0);
    if (hCompPort == NULL) {
        wprintf(L"CreateIoCompletionPort failed with error: %u\n",
            GetLastError() );
        WSACleanup();
        return 1;
    }
            
    // Create a listening socket
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"Create of ListenSocket socket failed with error: %u\n",
            WSAGetLastError() );
        WSACleanup();
        return 1;
    }

    // Associate the listening socket with the completion port
    CreateIoCompletionPort((HANDLE) ListenSocket, hCompPort, (u_long) 0, 0);

    //----------------------------------------
    // Bind the listening socket to the local IP address
    // and port 27015
    port = 27015;
    thisHost = gethostbyname("");
    ip = inet_ntoa(*(struct in_addr *) *thisHost->h_addr_list);

    service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr(ip);
    service.sin_port = htons(port);

    if (bind(ListenSocket, (SOCKADDR *) & service, sizeof (service)) == SOCKET_ERROR) {
        wprintf(L"bind failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    //----------------------------------------
    // Start listening on the listening socket
    iResult = listen(ListenSocket, 100);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"listen failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    wprintf(L"Listening on address: %s:%d\n", ip, port);

    // Load the AcceptEx function into memory using WSAIoctl.
    // The WSAIoctl function is an extension of the ioctlsocket()
    // function that can use overlapped I/O. The function's 3rd
    // through 6th parameters are input and output buffers where
    // we pass the pointer to our AcceptEx function. This is used
    // so that we can call the AcceptEx function directly, rather
    // than refer to the Mswsock.lib library.
    iResult = WSAIoctl(ListenSocket, SIO_GET_EXTENSION_FUNCTION_POINTER,
             &GuidAcceptEx, sizeof (GuidAcceptEx), 
             &lpfnAcceptEx, sizeof (lpfnAcceptEx), 
             &dwBytes, NULL, NULL);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"WSAIoctl failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Create an accepting socket
    AcceptSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (AcceptSocket == INVALID_SOCKET) {
        wprintf(L"Create accept socket failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Empty our overlapped structure and accept connections.
    memset(&olOverlap, 0, sizeof (olOverlap));

    bRetVal = lpfnAcceptEx(ListenSocket, AcceptSocket, lpOutputBuf,
                 outBufLen - ((sizeof (sockaddr_in) + 16) * 2),
                 sizeof (sockaddr_in) + 16, sizeof (sockaddr_in) + 16, 
                 &dwBytes, &olOverlap);
    if (bRetVal == FALSE) {
        wprintf(L"AcceptEx failed with error: %u\n", WSAGetLastError());
        closesocket(AcceptSocket);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Associate the accept socket with the completion port
    hCompPort2 = CreateIoCompletionPort((HANDLE) AcceptSocket, hCompPort, (u_long) 0, 0); 
    // hCompPort2 should be hCompPort if this succeeds
    if (hCompPort2 == NULL) {
        wprintf(L"CreateIoCompletionPort associate failed with error: %u\n",
            GetLastError() );
        closesocket(AcceptSocket);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    
    // Continue on to use send, recv, TransmitFile(), etc.,.
    //...

    return 0;
}


Примечания по QoS

Функция TransmitFile позволяет задать два флага, TF_DISCONNECT или TF_REUSE_SOCKET, которые возвращают сокет в состояние "отсоединен, повторно используется" после передачи файла. Эти флаги не следует использовать в сокете, в котором запрашивается качество обслуживания, так как поставщик услуг может немедленно удалить любое качество обслуживания, связанное с сокетом, до завершения передачи файла. Лучший подход для сокета с поддержкой QoS — просто вызывать функцию closesocket после завершения передачи файлов, а не полагаться на эти флаги.

Примечания для банкомата

Существуют важные проблемы, связанные с настройкой подключения при использовании асинхронного режима передачи (ATM) с сокетами Windows 2. Важные сведения о настройке подключения atm см. в разделе Примечания в документации по функции принятия .

Требования

Требование Значение
Минимальная версия клиента Windows 8.1, Windows Vista [классические приложения | Приложения UWP]
Минимальная версия сервера Windows Server 2003 [классические приложения | Приложения UWP]
Целевая платформа Windows
Header winsock.h (включая Mswsock.h)
Библиотека Mswsock.lib
DLL Mswsock.dll

См. также раздел

GetAcceptExSockaddrs

GetOverlappedResult

GetQueuedCompletionStatus

ПЕРЕКРЫВАЮЩИХСЯ

TransmitFile

Функции Winsock

Справочник по Winsock

Принять

closesocket

getsockopt

listen

sockaddr