Функция AcceptEx (mswsock.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 Sockets 2. Важные сведения о настройке подключения atm см. в разделе Примечания в документации по функции принятия .

Требования

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

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

GetAcceptExSockaddrs

GetOverlappedResult

GetQueuedCompletionStatus

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

TransmitFile

Функции Winsock

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

Принять

closesocket

getsockopt

listen

sockaddr