.NET WebSockets se cerró por la fuerza a pesar de mantener vivo y actividad en la conexión

Hemos escrito un cliente simple de WebSocket usando System.Net.WebSockets. KeepAliveInterval en ClientWebSocket se establece en 30 segundos.

La conexión se abre con éxito y el tráfico fluye como se esperaba en ambas direcciones, o si la conexión está inactiva, el cliente envía solicitudes Pong cada 30 segundos al servidor (visible en Wireshark).

Pero después de 100 segundos, la conexión se termina abruptamente debido a que el socket TCP se cierra en el extremo del cliente (observando en Wireshark vemos que el cliente envía un FIN). El servidor responde con un 1001 Going Away antes de cerrar el socket.

Después de mucho excavar, hemos rastreado la causa y encontramos una solución bastante dura. A pesar de muchas búsquedas en Google y Stack Overflow, solo hemos visto un par de otros ejemplos de personas publicando sobre el problema y nadie con una respuesta, por lo que estoy publicando esto para salvar a otros del dolor y con la esperanza de que alguien pueda para sugerir una mejor solución.

La fuente del tiempo de espera de 100 segundos es que WebSocket usa un System.Net.ServicePoint, que tiene una propiedad MaxIdleTime para permitir que se cierren los sockets inactivos. Al abrir WebSocket, si hay un ServicePoint existente para el Uri, lo usará, independientemente de la propiedad MaxIdleTime establecida en la creación. De lo contrario, se creará una nueva instancia de ServicePoint, con MaxIdleTime establecido a partir del valor actual de la propiedad System.Net.ServicePointManager MaxServicePointIdleTime (cuyo valor predeterminado es 100,000 milisegundos).

El problema es que ni el tráfico de WebSocket ni el mantenimiento de WebSocket (Ping / Pong) parecen registrarse como tráfico en lo que respecta al temporizador de inactividad de ServicePoint. Entonces, exactamente 100 segundos después de abrir el WebSocket, simplemente se destruye, a pesar del tráfico o de la actividad.

Nuestro presentimiento es que esto puede deberse a que WebSocket comienza su vida como una solicitud HTTP que luego se actualiza a un websocket. Parece que el temporizador inactivo solo está buscando tráfico HTTP. Si eso es lo que está sucediendo, parece un error importante en la implementación de System.Net.WebSockets.

La solución que estamos usando es establecer MaxIdleTime en ServicePoint en int.MaxValue. Esto permite que WebSocket permanezca abierto indefinidamente. Pero la desventaja es que este valor se aplica a cualquier otra conexión para ese ServicePoint. En nuestro contexto (que es una prueba de carga con Visual Studio Web y prueba de carga) tenemos otras conexiones (HTTP) abiertas para el mismo ServicePoint, y de hecho ya hay una instancia activa de ServicePoint cuando abrimos nuestro WebSocket. Esto significa que después de actualizar MaxIdleTime, todas las conexiones HTTP para la prueba de carga no tendrán tiempo de inactividad. Esto no se siente muy cómodo, aunque en la práctica el servidor web debería estar cerrando conexiones inactivas de todos modos.

También exploramos brevemente si podríamos crear una nueva instancia de ServicePoint reservada solo para nuestra conexión WebSocket, pero no pudimos ver una forma limpia de hacerlo.

Otro pequeño giro que hizo que sea más difícil de rastrear es que, aunque la propiedad System.Net.ServicePointManager MaxServicePointIdleTime tiene un valor predeterminado de 100 segundos, Visual Studio anula este valor y lo establece en 120 segundos, lo que dificulta la búsqueda.

Respuestas a la pregunta(1)

Su respuesta a la pregunta