2011-01-11 9 views
33

Naturalmente, BeginReceive() nunca terminará si no hay datos. MSDN suggests que llamando al Close() abortaría BeginReceive().Cómo cancelar el socket BeginReceive()?

Sin embargo, llamando Close() en el zócalo también realiza una Dispose() en él, tal como figuraba en this great ansewr, y por lo tanto EndReceive() lanzaría una excepción porque el objeto ya está dispuesto (y lo hace!).

¿Cómo debo proceder?

+0

http://stackoverflow.com/questions/1921611/c-how-do-i-terminate-a-socket-before-socket-beginreceive-calls-back – SwDevMan81

Respuesta

40

Parece que esto es por (el muy tonto) diseño. Debe tener esta excepción lanzada y atrapada en su código.

MSDN ve silencio al respecto en efecto, pero si nos fijamos en la documentación de otro método de toma asíncrono, BeginConnect(), esto es lo que encontramos:

Para cancelar una llamada en espera a la BeginConnect método(), cierre el conector . Cuando se llama al método Close() mientras se está llevando a cabo una operación asíncrona , la devolución de llamada proporcionada al método BeginConnect() es llamada. Una llamada posterior al método EndConnect (IAsyncResult) dará arrojar una excepción ObjectDisposed a indicando que la operación ha sido cancelada.

Si es la forma correcta de hacer para BeginConnect, probablemente sea así también para BeginReceive. Este es sin duda un diseño deficiente por parte de la API asíncrona de Microsoft, porque hacer que el usuario tire necesariamente y atrape la excepción como parte de un flujo normal molestaría al depurador. Realmente no tiene forma de "esperar" hasta que se complete la operación, porque Close() es lo que la completa en primer lugar.

+4

No puedo creer que esté diseñado para funcionar así, pero es lo que es, supongo. – Kelly

+0

@Kelly: ¿Qué tiene de malo como diseño? El código que llama a 'EndReceive' necesitará manejar fallas independientemente de si se lanza una excepción o no. Si la condición se puede manejar directamente en el 'EndReceive', no tener que lanzar una excepción permitiría que un' catch' sea reemplazado por un 'if', pero si no se puede manejar entonces, uno debería agregar' if ([falló]) throw [algo] 'y would * still * need a catch. Las conexiones no se deben interrumpir con la frecuencia suficiente para que la sobrecarga de rendimiento de usar una excepción frente a un 'si' importe. – supercat

+4

Porque lanzar y atrapar una excepción como parte de una entrada normal, a diferencia de un flujo "excepcional", es un mal diseño, independientemente de las implicaciones de rendimiento. También puede molestar a la depuración en la configuración de "interrupción de lanzamiento", que a menudo es útil. –

-1

Otra solución sería enviar "usted mismo" un "mensaje de control" utilizando un socket conectado a un puerto diferente. No es exactamente un aborto, pero pondría fin a su operación asincrónica.

+0

También podrían usar un identificador de espera o evento. También podrían usar opciones como indiqué en mi respuesta. Esto requiere otro socket por cliente y servidor. – Jay

-1

Tuve problemas con esto también, pero hasta donde puedo decir usando un indicador booleano simple antes de llamar al .BeginReceive() también funcionará (por lo que no será necesario el manejo de excepciones). Como ya tenía el manejo de inicio/detención, esta solución era una cuestión de una sola declaración if (desplácese hacia abajo hasta la parte inferior del método OnReceive()).

if (_running) 
{ 
    _mainSocket.BeginReceive(_data, 0, _data.Length, SocketFlags.None, OnReceive, null); 
}     

Debería haber pasado por alto algo con este enfoque, hágamelo saber!

+0

Esto no funciona si quiere abortar DESPUÉS de llamar a BeginReceive. Dado que BeginReceive crea una devolución de llamada asincrónica, tendrá la devolución de llamada asincrónica esperando hasta que haya datos para recibir. ¿Qué sucede si quiere abortar después de llamar a BeginReceive pero antes de que se ejecute la devolución de llamada Async (que es lo que sugiere su pregunta)? Su solución no funcionaría en este caso. –

+0

Por lo tanto, se necesita el manejo de excepciones en el caso del OP, ya que la única manera de cancelar la devolución de llamada asincrónica es cerrar el socket. Lo cual provocará que una ObjectDisposedException sea lanzada en EndReceive. –

2

Estoy sorprendido de que nadie haya recomendado usar SocketOptions.

Una vez que la pila tiene la operación de envío o recepción, está sujeta a las opciones de socket del socket.

Utilice un tiempo de espera de envío o recepción pequeño y utilícelo antes de la operación para que no le importe si se cambió durante la misma operación a algo más corto o más largo.

Esto causará más cambio de contexto pero no requerirá cerrar el socket bajo ningún protocolo.

Por ejemplo:

1) Establecer un pequeño tiempo de espera

2) Realizar operaciones

3) Ajuste de tiempo de espera más grande

Esto es similar al uso de bloqueo = false pero con una automática tiempo de espera que especifique.

+1

Las opciones de tiempo de espera de envío/recepción de socket solo se aplican mientras está en curso una operación de E/S de bloqueo. No he analizado cómo se implementa la E/S asíncrona del Framework, pero espero que sea con un mecanismo asíncrono real como WSASelect o puertos de finalización de E/S, y no con algún hilo sentado en una operación de bloqueo. –

0

Para conexiones de socket TCP, puede utilizar la propiedad Connected para determinar el estado de la toma de corriente antes de intentar acceder cualquier método eliminado Por MSDN:

"La propiedad Connected obtiene el estado de conexión del Socket a partir de la última operación de E/S. Cuando devuelve false, el Socket nunca se conectó o ya no está conectado."

Dado que dice "ya no está conectado" implica que se llamó previamente a Close() en el socket. Si comprueba si el socket está Conectado al inicio de la devolución de llamada de recepción, no habrá excepción.

+1

Se da cuenta de que estos conectores asíncronos están dentro de un programa multiproceso, ¿no? Puede verificar una línea en el hilo A y luego la ejecución puede cerrarse en el hilo B, y luego en el hilo A, su condición está fuera de la ventana. –

Cuestiones relacionadas