Acerca de...
El equipo
Encuestas
WinTablets

Encuesta

Si las WINTABLETs no pudiesen ejecutar aplicaciones de escritorio, ¿las seguirías usando?

Cargando ... Cargando ...

últimas entradas importantes

Categorías

Archivos

13
May 2015
CrashesDesarrollorfogdevSocketstrucos

Sockets (o cualquiero otro recurso nativo) que no se libera en C#

Esta es rápida y la escribo por tenerla de recordatorio, porque me ha estado tocando las conjeturas epilépticas bastante a fondo y la solución, una vez comprendida, es evidente por sí misma.

Supongamos que tenemos que usar un Socket UDP para hablar con un dispositivo remoto. Ya sé que los UDP no son muy recomendados en general, pero a veces son útiles, sobre todo cuando lo que se transmite no son datos exactos y si se pierde una parte no pasa nada.

¿Dónde se usa eso? Pues el ejemplo típico son las llamadas IP. Ahí, si pierdes varios paquetes la voz no se pierde aunque se va volviendo más metálica o se entrecorta según sea el protocolo utilizado sobre el UDP.

Actualmente, tener varios interfaces de red en un mismo equipo es bastante habitual. Tenemos un WiFi, una o dos conexiones de red cableada, otra por Bluetooth, sin contar los loopback, túneles y demás variaciones. Por poneros un ejemplo, mi equipo de desarrollo Windows tiene 8 interfaces de red. Más lo que cree yo para hablar con las máquinas virtuales.

Ahora supongamos que tengo que enviar paquetes UDP a uno de esos interfaces de red, pero no sé cuál de ellos es, por lo que hay que ir probando uno detrás de otro hasta que lo que haya en remoto responda.

La tarea es bastante sencilla: creamos un UdpClient, asignándole un IPEndPoint y el puerto correspondiente. Luego utilizaremos el método Send de m_sendClient para enviar:

cap1

Ese código lo ejecutamos en bucle hasta que el dispositivo nos responda. El ajuste del timeout es para que podamos esperar la respuesta de forma síncrona cuando usemos el método Receive().

¿Qué pasa si en una pasada no hemos encontrado el dispositivo? ¿Qué pasa si la aplicación quiere volver a escanear?

El código de muestra es una parte de una clase completa, con su método Close() y demás para evitar el chorreo de recursos.

Por lo tanto, en teoría, si volvemos a crear un nuevo objeto de esa clase, habiendo liberado el anterior ya sea por salida de ámbito, por volver a asignarlo a la misma variable, o por asignar la variable a null, no debería haber ningún problema.

Todos los recursos nativos utilizados, como el socket, el IPEndPoint, y todo lo anexo se debería haber liberado.

¡¡Pues no!!

Cuando vuelves a intentar escanear la misma IP y el mismo puerto para un interfaz ya escaneado, no solo te salta una excepción sobre la imposibilidad de asignar recursos ya asignados, sino que también se producen otras sobre recursos disposed, mientras que algunas partes de los objetos internos están asignados a null o tienen valores incorrectos.

A todas luces es un bug del .NET, ya que con otros recursos como ficheros no suele pasar ya que cuando el objeto sale de ámbito, el recurso nativo se libera aunque la memoria no. O al menos es lo que debería pasar.

Así que uno debe llamar al recolector de basura antes volver a crear estos objetos, o más bien hacerlo en bucle hasta que no salte ninguna excepción. No lo digo yo, lo dice Microsoft:

cap2

El enlace a la respuesta es este: https://social.msdn.microsoft.com/Forums/en-US/04b543b0-1569-430b-ad9b-16f468627ede/socketexception-when-registering-channel-possible-bug-in-net-remoting?forum=netfxremoting

Por lo tanto, la solución a este (y otros problemas similares), es esta:

cap3

Bueno, faltaría algún tipo de timeout para evitar dejar enganchado ahí al programa.

Ah, iba a titular la entrada “Los recursos nativos y el puto recolector de basura del .NET”, pero me lo he pensado mejor. Y sí, algo de recochineo también hay. :-)

Por RFOG | 2 Comentarios | Enlaza esta entrada

2 Comentarios

Pon
Enviado el 15/05/2015 a las 00:43 | Permalink

Veo que udpclient es IDisposable.

No veo que lo utilices con using.

¿En el close ese que comentas llamas a Dispose?

¿Se arreglaria el tema llamandolo?

    RFOG
    Enviado el 15/05/2015 a las 09:02 | Permalink

    No se puede llamar. Es lo primero que uno prueba en esos casos. Lo más que puedes hacer es asignarlo manualmente a null y a correr. Además, using no lo puedo usar porque tengo que abrirlo, usarlo y luego cerrarlo en distintas llamadas. Ya lo he comentado por otros lares: el sistema de liberación de recursos de C#, con los Dispose y sus variaciones, es algo bastante aberrante teniendo la solución completamente limpia de C++/CLI, con la separación entre finalizador y destructor, amén de que en C++/CLI la destrucción de recursos es determinista, cosa que no entiendo por qué no la han implementado en C# si está en el otro lenguaje (personalmente creo que el amigo Helsberg debía haberse quedado con su Delphi).

    Pero se da otro problema todavía más grave que no he puesto en la entrada, y es que después de cerrado… si estás recibiendo en modo asíncrono, te salta el evento de recepción y ya te puedes imaginar la que se lía.

    Si tuviera más tiempo y más ganas miraría el código fuente de la clase, vería el error que está cometiendo Microsoft y escribiría una de esas entradas mías en las que me río de ellos por ser tan torpes (en mi época de geeks.ms).

    Pero ni tengo ganas ni casi tiempo, y luego al final del todo podría ser otra cosa.

    En fin, es lo que hay. Otra chapucería más por culpa de Microsoft.

Deja un comentario  

Tu email nunca se publica o se comparte. Los campos obligatorios están marcados con *

*
*
:wink: :-| :-x :twisted: :) 8-O :( :roll: :-P :oops: :-o :mrgreen: :lol: :idea: :-D :evil: :cry: 8) :arrow: :-? :?: :!:
Puedes usar las siguientes etiquetas y atributos HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

contacto@wintablet.info tema WinTablet.info por Ángel García (Hal9000)