2011-03-02 8 views
9

que tienen un oyente zócalo de la que cuelga sobre la función recv:cómo interrumpir un hilo que está esperando la función recv?

size_t recvLen = recv(sock, buf, 512, 0); 

me gustaría terminar este hilo con interrumpirla. MSDN dice:

Al emitir una llamada de Winsock bloqueo como recv, Winsock puede que tenga que esperar para un evento de red antes de la llamada puede completar. Winsock realiza una espera de alertable en esta situación, que se puede interrumpir mediante una llamada a procedimiento asincrónico (APC) programada en el mismo subproceso.

¿Cómo puedo hacer eso?

+0

use closesocket mediante la cola de espera de un APC en el hilo, que causará que recv regrese con un error porque el socket está cerrado – paulm

Respuesta

9

Puede interrumpirlo haciendo cola con un APC a través del QueueUserAPC. Sin embargo, lo más probable es que no sea seguro finalizar el hilo en el APC. Poner en cola un APC no termina el recv, solo lo interrumpe; una vez que el APC regrese, volverá a esperar en recv nuevamente.

Si desea detener completamente el recv, debe usar select con un tiempo de espera para esperar hasta que haya datos disponibles. Luego puede verificar si debe seguir esperando datos o continuar en cada tiempo de espera.

+2

llama a closesocket() en la llamada de APC y volverá, sin necesidad de toma asíncrona o mal llamadas TerminateThread :) – paulm

-1

Errrr ... ¿Realizar un APC en el mismo hilo? :-))

Hablando en serio, no está claro cuál es su objetivo aquí.
Si solo desea finalizar el hilo, use la función TerminateThread.
Si desea interrumpir esta llamada en particular, puede cerrar el socket.

+0

no se debe utilizar el hilo de terminación ya que no notifica las DLL y no permite la limpieza del hilo. Es posible cerrar el zócalo, pero es posible que aún necesite el zócalo. –

0

Creo que la mejor manera de manejar este problema es poner el zócalo en modo E/S sin bloqueo, para que el hilo nunca se bloquee dentro de recv() (o en enviar(), para el caso). El hilo solo debe bloquear dentro de select() (o WaitMultipleObjects()). De esta forma, la llamada select() (o WaitMultipleObjects()) volverá si los datos llegan para el socket (en cuyo caso puede llamar a recv() para obtener los nuevos datos sin bloquear), pero también puede tener select()/WaitMultipleObjects() regresa cuando sucede algo más; p.ej. cuando recibe un mensaje del hilo principal. Si está utilizando select(), ese aviso puede ser el hilo principal que envía un byte en un par de sockets diferente (con el hilo principal sosteniendo un extremo del par de sockets, y el hilo de E/S sosteniendo el otro extremo); si está utilizando WaitMultipleObjects() entonces creo que puede usar cualquiera de los métodos estándar de evento/señalización de Windows que causarían que WaitMultipleObjects() devuelva.

+1

No necesitas poner el socket en modo no-bloqueo para usar 'select()'. Puede usar 'select()' con sockets de bloqueo. Simplemente llame a 'select()' para asegurarse de que el socket sea legible antes de llamar a 'recv()', luego 'recv() no se bloqueará incluso si el socket está en modo de bloqueo. –

+0

No estoy seguro de cómo es eso más fácil/mejor que poner el zócalo en modo sin bloqueo. Ahora, en lugar de agregar una línea de código, debe agregar una línea de código antes de cada llamada potencialmente bloqueadora al socket (y si usted, o los futuros mantenedores de código, alguna vez se olvidan de hacerlo, tendrá un aspecto sutil y difícil). para detectar errores en su programa que pueden costarle a alguien mucho tiempo rastrear) –

+0

Además, el enfoque check-with-select-before-recv() no siempre funciona de manera confiable: http://stackoverflow.com/ a/5352634/131930 –

3

Si no desea recibir más datos, puede eliminar el socket en cualquier momento. Simplemente llame a close(), la función en cuestión devolverá inmediatamente un error.

Lo que he hecho en el pasado es simplemente ejecutar otro hilo con un tiempo de espera, después del período de espera si un indicador de "no morir" no está configurado matar el socket.

1

Comprobando la memoria intermedia del zócalo anterior a recv es más flexible en lugar de cubrir un lote para el soporte select(), creo. Puede llamar al ioctlsocket(SockHandle, FIONREAD, Longint(CountInBuffer)) para ver si hay datos en el búfer de red para leer y luego llamar al recv(SockHandle, buff, CountInBuffer, 0). De esta forma, puede realizar una única llamada recv para leer todo el búfer de lectura de la red si asigna suficiente buff con CountInBuffer.De lo contrario, debe llamar a recv en un bucle para leer el búfer de red, que es la forma tradicional. En ambos casos, todavía está en las restricciones del CountInBuffer.

-1

La única manera de interrumpir realmente una llamada de bloqueo recv() y hacer que salga por completo es cerrar el socket desde otro contexto de subproceso que el que está bloqueado. Si eso no es una opción, entonces necesita volver a escribir su lógica de socket. Es mejor usar E/S no bloqueante o asíncrona, de esa manera recv() (o WSARecv() para este último) nunca se bloqueará, y puede hacer lo que necesite (como verificar las condiciones de finalización del hilo) mientras la lectura se realiza en fondo.

+0

Solo asegúrese de no tener una condición de carrera donde el otro hilo ya podría haberlo cerrado, o puede estar cerrando un descriptor de socket abierto para otro socket (un nuevo socket) ya [? ] – rogerdpack

+0

re: async aparentemente eso es posible usando WaitForMultipleObjects http://ffmpeg.org/pipermail/ffmpeg-devel/2013-December/152211.html – rogerdpack

+0

No puede usar ninguna de las funciones 'WaitFor ... Object (s)' con manijas del zócalo. Tendría que usar identificadores de evento de 'CreateEvent()' en su lugar, llamando 'SetEvent()' cuando las operaciones de socket se completan. O use 'WSACreateEvent()' con 'WSAEventSelect()' y 'WSAWaitForMultipleEvents()'. O use 'CreateIoCompletionPort()' con 'GetQueuedCompletionStatus()'. –

Cuestiones relacionadas