Acerca de...
El equipo
Encuestas
Política de privacidad
WinTablets

Encuesta

¿Cual crees que triunfará?

Ver Resultados

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
contacto@wintablet.info tema WinTablet.info por Ángel García (Hal9000)