Socket de E/S superpuesto frente al modo de bloqueo/no bloqueo


Resumen


En este artículo se explica la diferencia entre el atributo de E/S superpuesto de un socket y el modo de bloqueo o no bloqueo del socket.

Más información


Cuando se crea un socket, de forma predeterminada es un socket de bloqueo. Puede utilizar el comando FIONBIO en la llamada a la API ioctlsocket, WSAEventSelect o WSAAysncSelect para cambiar el modo de socket de bloqueo a no bloqueo. Si una llamada de Winsock no se puede completar inmediatamente, se produce un error en la llamada y WSAGetLastError devuelve un error WSAEWOULDBLOCK si se trata de un socket sin bloqueo o la llamada se bloquea hasta que se completa la operación si se trata de un socket de bloqueo. El atributo de E/S superpuesto del socket es diferente del modo de bloqueo o no bloqueo del socket. Aunque la implementación actual de Winsock requiere un atributo de E/S superpuesto para el modo de socket sin bloqueo, son conceptualmente independientes y su modelo de programación también es diferente. Para crear un socket con el atributo de E/S superpuesto, puede utilizar la API de socket o la API WSASocket con el conjunto de indicadores WSA_FLAG_OVERLAPPED. Si una operación de E/S superpuesta no se puede completar inmediatamente, se produce un error en la llamada y WSAGetLastError o GetLastError devuelven WSA_IO_PENDING o ERROR_IO_PENDING, que en realidad es el mismo definido que WSA_IO_PENDING. Para obtener información adicional, consulte el siguiente artículo en Microsoft Knowledge Base:
179942 INFO: WSA_FLAG_OVERLAPPED es necesario para sockets sin bloqueo
Tenga en cuenta que una vez que se crea un socket, no hay manera de cambiar el atributo superpuesto del socket. Sin embargo, puede llamar a la API setsockopt con la opción SO_OPENTYPE en cualquier identificador de socket, incluido UN INVALID_SOCKET, para cambiar los atributos superpuestos para todas las llamadas de socket sucesivas en el mismo subproceso. El valor predeterminado de la opción SO_OPENTYPE es 0, que establece el atributo superpuesto. Todos los valores de opción distintos de cero hacen que el socket sea sincrónico y lo hacen para que no pueda utilizar una función de finalización. Al establecer el atributo de E/S superpuesto de un socket, no significa que el socket realizará una operación de E/S superpuesta. Por ejemplo, si usted especifica NULL para la función de finalización y la estructura solapada en WSARecv y WSASend, o usted simplemente llama a las funciones recv o send, se completarán de una manera de bloqueo. Para asegurarse de que la E/S se realiza de forma superpuesta, debe proporcionar una estructura superpuesta en la función de E/S, dependiendo de la función que utilice.

E/S superpuestas

En Winsock 1, se crea un socket superpuesto mediante la API de socket y se usa la API de E/S de archivos Win32 ReadFile, ReadFileEx, WriteFile, WriteFileEx para realizar E/S superpuestas en el identificador de socket. En Winsock 2, se crea un socket superpuesto mediante WSASocket con el indicador WSA_FLAG_OVERLAPPED, o simplemente mediante la API de socket. Usted puede utilizar las API de E/S del archivo Win32 antedichas o Winsock 2 WSASend, WSASendTo, WSARecv, y WSARecvFrom para iniciar una operación de E/S superpuesta. Si utiliza la opción SO_RCVBUF y SO_SNDBUF para establecer el búfer de recepción y envío de pila TCP cero, básicamente indica a la pila TCP que realice directamente E/S mediante el búfer proporcionado en la llamada de E/S. Por lo tanto, además de la ventaja de no bloqueo de la E/S de socket superpuesto, la otra ventaja es un mejor rendimiento porque guarda una copia de búfer entre el búfer de pila TCP y el búfer de usuario para cada llamada de E/S. Pero debe asegurarse de no tener acceso al búfer de usuario una vez que se envía para la operación superpuesta y antes de que se complete la operación superpuesta. Para determinar si se ha completado la operación de E/S superpuesta, puede utilizar una de las siguientes opciones:
  • Puede proporcionar un identificador de evento en una estructura superpuesta utilizada en la llamada de E/S y esperar en el identificador de evento para señalar.
  • Use GetOverlappedResult o WSAGetOverlappedResult para sondear el estado de la operación de E/S superpuesta. En Windows NT, puede especificar NULL como identificador de evento en la estructura superpuesta. Sin embargo, en Windows 95 la estructura superpuesta debe contener un identificador de evento de restablecimiento manual. WSAGetOverlappedResult en Windows 98 y Windows Millennium Edition (ME) se modifica de forma que se comporte como Windows NT y también puede sondear el estado de finalización con un identificador de evento NULL en la estructura superpuesta.
  • Utilice ReadFileEx, WriteFileEx, WSARecv, WSARecvFrom, WSASend o WSASendTo y elija proporcionar una función de finalización a la que se llamará cuando se haya completado la operación de E/S superpuesta. Si usted utiliza el enfoque de la función de finalización, en algún momento después de que usted haya publicado la operación de E/S solapada usted necesita publicar una función de espera Win32 o una versión WSA de la función de espera para esperar en un manejador no señalado para poner su hilo actual en un estado de espera alerta. Cuando se completa la operación de E/S superpuesta, se llama a la función de finalización, la función de espera va a devolver WAIT_IO_COMPLETION y el subproceso de espera alertado se despierta.
  • Utilice GetQueuedCompletionStatus y asocie un socket, junto con el conjunto de atributos de E/S superpuestos, con un puerto de finalización de E/S de Windows NT (IOCP). Con IOCP, no es necesario proporcionar una función de finalización, esperar en un identificador de evento para indicar o sondear el estado de la operación superpuesta. Una vez que cree el IOCP y agregue el identificador de socket superpuesto al IOCP, puede iniciar la operación superpuesta utilizando cualquiera de las API de E/S mencionadas anteriormente (excepto recv, recvfrom, send o sendto). Tendrá el bloque de subprocesos de trabajo en la API GetQueuedCompletionStatus esperando un paquete de finalización de E/S. Cuando se completa una E/S superpuesta, un paquete de finalización de E/S llega a los retornos IOCP y GetQueuedCompletionStatus. IOCP es la compatibilidad del sistema operativo Windows NT para escribir un servidor escalable y de alto rendimiento mediante subprocesos muy simples y bloquear código en operaciones de E/S superpuestas. Por lo tanto, puede haber una ventaja de rendimiento significativa del uso de E/S de socket superpuesto con IOCPs de Windows NT.

Modo de bloqueo y no bloqueo

Cuando se crea un socket, de forma predeterminada es un socket de bloqueo. En las operaciones de E/S de socket de modo de bloqueo, conecte y acepte las operaciones all block hasta que se complete la operación en cuestión. Para cambiar el modo de operación de socket del modo de bloqueo al modo de no bloqueo, puede utilizar WSAAsyncSelect, WSAEventSelect o el comando FIONBIO en la llamada a la API ioctlsocket. WSAAsyncSelect asigna notificaciones de socket a mensajes de Windows y es el mejor modelo para una sola aplicación GUI de subprocesos. WSAEventSelect utiliza WSAEnumNetworkEvents para determinar la naturaleza de la notificación de socket en el evento de señalización y asigna notificaciones de socket mediante la señalización de un evento. Este es un modelo útil para aplicaciones que no son GUI que carecen de una bomba de mensajes, como una aplicación de servicio de Windows NT. El comando FIONBIO en la llamada a la API ioctlsocket también pone el socket en modo sin bloqueo. Pero debe sondear el estado del socket mediante la API select. Actualización: no es necesario establecer SO_RCVBUF en cero con recv superpuesto para Windows 2000, que puede copiar directamente los datos en el búfer de recv de la aplicación. Establecer SO_SNDBUF en cero con el envío superpuesto en Windows 2000 sigue teniendo la misma ventaja de guardar una copia de memoria que en Windows NT 4.0. También es importante que la aplicación emita varios envíos superpuestos pendientes o recv si la aplicación establece el búfer de pila correspondiente en 0.