Rompiendo el bloqueo de ReadFile() – Tubería con nombre (API de Windows)

5 minutos de lectura

avatar de usuario
mike comerciante

Para simplificar, esta es una situación en la que un SERVIDOR NamedPipe está esperando que un CLIENTE NamedPipe escriba en la tubería (usando WriteFile())

La API de Windows que está bloqueando es ReadFile()

El servidor ha creado la canalización síncrona (sin E/S superpuestas) con el bloqueo habilitado

El cliente se ha conectado y ahora el servidor está esperando algunos datos.

En el flujo normal de cosas, el cliente envía algunos datos y el servidor los procesa y luego regresa a ReadFile() para esperar el siguiente fragmento de datos.

Mientras tanto, ocurre un evento (entrada del usuario, por ejemplo) y NamedPipe SERVER ahora debe ejecutar algún otro código, lo que no puede hacer mientras ReadFile() está bloqueando.

En este punto, debo mencionar que NamedPipe Client no es mi aplicación, por lo que no tengo control sobre ella. No puedo hacer que envíe algunos bytes para desbloquear el servidor. Simplemente se quedará allí y no enviará datos. Como no tengo el control de la implementación del Cliente, no puedo cambiar nada al respecto.

Una solución sería crear un subproceso separado en el que se realicen todas las operaciones ReadFile(). De esa manera, cuando ocurra el evento, solo puedo procesar el código. El problema con eso es que el evento también requiere un subproceso separado, por lo que ahora tengo dos subprocesos adicionales para cada instancia de este servidor. Dado que esto debe ser escalable, esto no es deseable.

Desde otro hilo he intentado llamar

 DisconnectNamedPipe()

y

 CloseHandle()

ambos no regresarán (hasta que el cliente escriba en la canalización).

No puedo conectarme a la misma tubería y escribir algunos bytes porque:

“Todas las instancias de una canalización con nombre comparten el mismo nombre de canalización, pero cada instancia tiene sus propios búferes y controladores, y proporciona un conducto separado para la comunicación cliente/servidor”.

http://msdn.microsoft.com/en-us/library/aa365590.aspx

Necesito una forma de falsificarlo, así que la pregunta de $64k dólares es:

¿Cómo puedo romper el bloqueo de ReadFile()?

Prueba esto antes de ReadFile:

BOOL WINAPI PeekNamedPipe(
  __in       HANDLE hNamedPipe,
  __out_opt  LPVOID lpBuffer,
  __in       DWORD nBufferSize,
  __out_opt  LPDWORD lpBytesRead,
  __out_opt  LPDWORD lpTotalBytesAvail,
  __out_opt  LPDWORD lpBytesLeftThisMessage
);

if(TotalBytesAvail > 0)
  ReadFile(....);

-AV-

  • Esto funciona como se anuncia, pero asume que hay datos en la tubería para leer. El problema es que necesitamos ReadFile() para bloquear HASTA que se envíen datos. Luego leemos los datos y volvemos al estado de bloqueo de ReadFile(). Si no usamos el bloqueo de ReadFile(0, entonces tendríamos que revisar constantemente la tubería (anulando el propósito del bloqueo en primer lugar)

    – Mike Trader

    6 de agosto de 2009 a las 10:25

avatar de usuario
alex2k8

Eche un vistazo a CancelSynchronousIo

Marca las operaciones de E/S sincrónicas pendientes emitidas por el subproceso especificado como canceladas.

Y CancelIo/CancelIoEx:

Para cancelar todas las operaciones de E/S asincrónicas pendientes, utilice:

CancelIo: esta función solo cancela las operaciones emitidas por el subproceso de llamada para el identificador de archivo especificado.

CancelIoEx: esta función cancela todas las operaciones emitidas por los subprocesos para el identificador de archivo especificado.

  • Oooh. Me perdí eso… Cliente mínimo compatible Windows Vista Servidor mínimo compatible Windows Server 2008 Desafortunadamente, este es Windows Server 2003. Maldición

    – Mike Trader

    27 de febrero de 2009 a las 5:19

  • Busque en Google el artículo ‘msdn Synchronous and Asynchronous I/O’. Parece que la única opción que queda es TerminateThread, pero esto sería una mala idea (google para ‘msdn TerminateThread puede dar lugar a los siguientes problemas’)

    – alex2k8

    27 de febrero de 2009 a las 10:30

  • Más información aquí: msdn.microsoft.com/en-us/library/aa480216.aspx (“Compatibilidad con la cancelación de E/S de Win32 en Windows Vista”).

    – dan-gph

    24 de mayo de 2009 a las 0:35

Miguel,

No puede cancelar ReadFile síncrono. Pero puede cambiar a operaciones asincrónicas (superpuestas). Al hacer esto, puede implementar una arquitectura bastante escalable.

Posible algoritmo (solo una idea):

  • Para cada nueva llamada de cliente ReadFile
  • WaitForMultipleObjects donde los identificadores se superponen.hEvent + sus eventos personalizados
  • Iterar sobre eventos señalados y programarlos para su ejecución por subprocesos de un grupo de subprocesos.

De esta manera, puede tener solo unos pocos subprocesos para recibir conexiones y leer datos, mientras que el procesamiento de datos real puede ser realizado por el grupo de subprocesos.

  • Sí. Esa es la siguiente etapa del diseño. desafortunadamente heredé la mayor parte de este problema. El IPC no está abierto para mí, ni tampoco la especificación FastCGI. Era una posibilidad remota, pero pensé en preguntar en caso de que alguien tuviera una técnica para romper el bloque.

    – Mike Trader

    27 de febrero de 2009 a las 2:04

avatar de usuario
Kevin

El problema con eso es que el evento también requiere un subproceso separado, por lo que ahora tengo dos subprocesos adicionales para cada instancia de este servidor. Dado que esto debe ser escalable, esto no es deseable.

Nunca en mi carrera he encontrado que “más subprocesos” == “menos escalable”. ¿Cuántas de estas instancias de “servidor” tienes?

Normalmente, una operación debe realizarse en un subproceso separado si esa operación se va a bloquear y el sistema debe responder mientras la operación está bloqueada.

Las operaciones de E/S asíncronas no tienen que bloquear ningún subproceso si utilizan puertos de finalización de E/S. Ver: http://msdn.microsoft.com/en-us/library/aa365198(VS.85).aspx

avatar de usuario
usuario3498796

Lo que sucede es que la tubería de salida del servidor se deja abierta esperando la conexión mientras su cliente intenta conectarse a la tubería de entrada del servidor (que ya no existe) … Lo que debe hacer es vaciar su tubería de salida para hacer un bucle de vuelta a su entrada. Puede salir del lado del cliente leyendo el archivo (recuerde hacer un bucle en el establecimiento de conexión porque hay un “apretón de manos” allí, y nunca funcionará la primera vez)

avatar de usuario
gil123

Solo usa SetNamedPipeHandleState función
https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-setnamedpipehandlestate

Utilizar el PIPE_NOWAIT bandera al llamar a esta función.

hNamedPipe debería ser el mango que regresó de CreateFile función.

Después de eso, la llamada a ReadFile no bloqueará el hilo cuando no haya datos disponibles.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad