Функция AcceptEx (mswsock.h)
Функция AcceptEx принимает новое подключение, возвращает локальный и удаленный адрес и получает первый блок данных, отправленный клиентским приложением.
Синтаксис
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 вместо функции 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)
Когда функция 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);
}
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 |