Función AcceptEx (mswsock.h)
La función AcceptEx acepta una nueva conexión, devuelve la dirección local y remota y recibe el primer bloque de datos enviados por la aplicación cliente.
Sintaxis
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
);
Parámetros
[in] sListenSocket
Descriptor que identifica un socket al que ya se ha llamado con la función de escucha . Una aplicación de servidor espera los intentos de conexión en este socket.
[in] sAcceptSocket
Descriptor que identifica un socket en el que se va a aceptar una conexión entrante. Este socket no debe estar enlazado ni conectado.
[in] lpOutputBuffer
Puntero a un búfer que recibe el primer bloque de datos enviados en una nueva conexión, la dirección local del servidor y la dirección remota del cliente. Los datos de recepción se escriben en la primera parte del búfer a partir de cero de desplazamiento, mientras que las direcciones se escriben en la última parte del búfer. Este parámetro debe especificarse.
[in] dwReceiveDataLength
Número de bytes en lpOutputBuffer que se usará para los datos de recepción reales al principio del búfer. Este tamaño no debe incluir el tamaño de la dirección local del servidor, ni la dirección remota del cliente; se anexan al búfer de salida. Si dwReceiveDataLength es cero, aceptar la conexión no dará lugar a una operación de recepción. En su lugar, AcceptEx se completa en cuanto llega una conexión, sin esperar a que se produzcan datos.
[in] dwLocalAddressLength
Número de bytes reservados para la información de dirección local. Este valor debe tener al menos 16 bytes más que la longitud máxima de la dirección para el protocolo de transporte en uso.
[in] dwRemoteAddressLength
Número de bytes reservados para la información de la dirección remota. Este valor debe tener al menos 16 bytes más que la longitud máxima de la dirección para el protocolo de transporte en uso. No puede ser cero.
[out] lpdwBytesReceived
Puntero a un DWORD que recibe el recuento de bytes recibidos. Este parámetro solo se establece si la operación se completa sincrónicamente. Si devuelve ERROR_IO_PENDING y se completa más adelante, este DWORD nunca se establece y debe obtener el número de bytes leídos desde el mecanismo de notificación de finalización.
[in] lpOverlapped
Estructura SUPERPUESTA que se usa para procesar la solicitud. Este parámetro debe especificarse; no puede ser NULL.
Valor devuelto
Si no se produce ningún error, se completa correctamente la función AcceptEx y se devuelve un valor de TRUE .
Si se produce un error en la función, AcceptEx devuelve FALSE. A continuación, se puede llamar a la función WSAGetLastError para devolver información de error extendida. Si WSAGetLastError devuelve ERROR_IO_PENDING, la operación se inició correctamente y sigue en curso. Si el error es WSAECONNRESET, se indicó una conexión entrante, pero el par remoto finalizó posteriormente antes de aceptar la llamada.
Comentarios
La función AcceptEx combina varias funciones de socket en una sola transición de API o kernel. La función AcceptEx , cuando se realiza correctamente, realiza tres tareas:
- Se acepta una nueva conexión.
- Se devuelven las direcciones locales y remotas de la conexión.
- Se recibe el primer bloque de datos enviados por el remoto.
Un programa puede establecer una conexión a un socket más rápidamente mediante AcceptEx en lugar de la función accept .
Un único búfer de salida recibe los datos, la dirección del socket local (el servidor) y la dirección del socket remoto (el cliente).
El uso de un único búfer mejora el rendimiento. Al usar AcceptEx, se debe llamar a la función GetAcceptExSockaddrs para analizar el búfer en sus tres partes distintas (datos, dirección de socket local y dirección de socket remoto). En Windows XP y versiones posteriores, una vez completada la función AcceptEx y la opción SO_UPDATE_ACCEPT_CONTEXT se establece en el socket aceptado, la dirección local asociada al socket aceptado también se puede recuperar mediante la función getsockname . Del mismo modo, la dirección remota asociada al socket aceptado se puede recuperar mediante la función getpeername .
El tamaño del búfer para la dirección local y remota debe ser de 16 bytes más que el tamaño de la estructura sockaddr para el protocolo de transporte en uso porque las direcciones se escriben en un formato interno. Por ejemplo, el tamaño de un sockaddr_in (la estructura de direcciones para TCP/IP) es de 16 bytes. Por lo tanto, se debe especificar un tamaño de búfer de al menos 32 bytes para las direcciones locales y remotas.
La función AcceptEx usa E/S superpuesta, a diferencia de la función accept . Si la aplicación usa AcceptEx, puede atender un gran número de clientes con un número relativamente pequeño de subprocesos. Al igual que con todas las funciones de Windows superpuestas, los eventos de Windows o los puertos de finalización se pueden usar como mecanismo de notificación de finalización.
Otra diferencia clave entre la función AcceptEx y la función accept es que AcceptEx requiere que el autor de la llamada ya tenga dos sockets:
- Uno que especifica el socket en el que se va a escuchar.
- Uno que especifica el socket en el que se va a aceptar la conexión.
El parámetro sAcceptSocket debe ser un socket abierto que no esté enlazado ni conectado.
El parámetro lpNumberOfBytesTransferred de la función GetQueuedCompletionStatus o la función GetOverlappedResult indica el número de bytes recibidos en la solicitud.
Cuando esta operación se completa correctamente, se puede pasar sAcceptSocket , pero solo a las siguientes funciones:
- ReadFile
- WriteFile
- enviar
- WSASend
- recv
- WSARecv
- TransmitFile
- closesocket
- setsockopt(only for SO_UPDATE_ACCEPT_CONTEXT)
Cuando se devuelve la función AcceptEx , el socket sAcceptSocket está en el estado predeterminado de un socket conectado. El socket sAcceptSocket no hereda las propiedades del socket asociado al parámetro sListenSocket hasta que se establece SO_UPDATE_ACCEPT_CONTEXT en el socket. Use la función setsockopt para establecer la opción SO_UPDATE_ACCEPT_CONTEXT, especificando sAcceptSocket como identificador de socket y sListenSocket como valor de opción.
Por ejemplo:
//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) );
Si se proporciona un búfer de recepción, la operación superpuesta no se completará hasta que se acepte una conexión y se lean los datos. Use la función getsockopt con la opción SO_CONNECT_TIME para comprobar si se ha aceptado una conexión. Si se ha aceptado, puede determinar cuánto tiempo se ha establecido la conexión. El valor devuelto es el número de segundos que el socket se ha conectado. Si el socket no está conectado, getsockopt devuelve 0xFFFFFFFF. Las aplicaciones que comprueban si se ha completado la operación superpuesta, en combinación con la opción SO_CONNECT_TIME, pueden determinar que se ha aceptado una conexión, pero no se han recibido datos. Examinar una conexión de esta manera permite a una aplicación determinar si las conexiones que se han establecido durante un tiempo no han recibido datos. Se recomienda que estas conexiones finalicen cerrando el socket aceptado, lo que obliga a que la llamada de función AcceptEx se complete con un error.
Por ejemplo:
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: esta función es compatible con las aplicaciones de Windows Phone Store en Windows Phone 8 y versiones posteriores.
Windows 8.1 y Windows Server 2012 R2: esta función es compatible con las aplicaciones de la Tienda Windows en Windows 8.1, Windows Server 2012 R2 y versiones posteriores.
Código de ejemplo
En el ejemplo siguiente se usa la función AcceptEx mediante puertos de E/S y finalización superpuestos.#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;
}
Notas de QoS
La función TransmitFile permite establecer dos marcas, TF_DISCONNECT o TF_REUSE_SOCKET, que devuelven el socket a un estado "desconectado y reutilizable" después de transmitir el archivo. Estas marcas no deben usarse en un socket en el que se haya solicitado calidad de servicio, ya que el proveedor de servicios puede eliminar inmediatamente cualquier calidad de servicio asociada al socket antes de que se haya completado la transferencia de archivos. El mejor enfoque para un socket habilitado para QoS es simplemente llamar a la función closesocket cuando se ha completado la transferencia de archivos, en lugar de confiar en estas marcas.Notas para ATM
Hay problemas importantes asociados con la configuración de conexión al usar el modo de transferencia asincrónica (ATM) con Windows Sockets 2. Consulte la sección Comentarios en la documentación de la función accept para obtener información importante sobre la configuración de la conexión ATM.Requisitos
Requisito | Value |
---|---|
Cliente mínimo compatible | Windows 8.1, Windows Vista [aplicaciones de escritorio | Aplicaciones para UWP] |
Servidor mínimo compatible | Windows Server 2003 [aplicaciones de escritorio | aplicaciones para UWP] |
Plataforma de destino | Windows |
Encabezado | mswsock.h (incluya Mswsock.h) |
Library | Mswsock.lib |
Archivo DLL | Mswsock.dll |