Antes de que todos marquen esto como un dup, permítanme decir que conozco una parte justa de la programación de redes y esta pregunta es mi intento de resolver algo que me intriga incluso después de encontrar la "solución".
Pasé las últimas semanas escribiendo un código de pegamento para incorporar un gran sistema industrial a nuestra configuración actual. El sistema está controlado por una computadora con Windows XP (PC A) que se controla desde un sistema Ubuntu 14.04 (PC B) mediante el envío de un flujo constante de paquetes UDP a 2000 Hz. Responde con paquetes UDP que contienen el estado actual del sistema.
Se tuvo cuidado para garantizar que se mantuviera la frecuencia de 2000 Hz porque hay un tiempo de espera de 3 ms después del cual el sistema falla y vuelve a un estado seguro. Esto implica medir y contabilizar las imprecisiones en std::this_thread::sleep_for . Las mediciones muestran que solo hay una derivación del 0,1% de la tasa objetivo.
Los problemas comenzaron cuando comencé a recibir la respuesta del estado del sistema. El lado de control en la PC B se ve más o menos así:
forever at 2000Hz { send current command; if ( socket.available() >= 0 ) { receive response; } }edición 2: O en código real:
auto cmd_buf = ... auto rsp_buf = ... while (true) { // prepare and send command buffer cmd_buf = ... socket.send(cmd_buf, endpoint); if (socket.available() >= 0) { socket.receive(rsp_buf); // the results are then parsed and stored, nothing fancy } // time keeping }El problema es que, cada vez que la parte de recepción del código estaba presente en la PC B , la PC A comenzó a quedarse sin memoria en cuestión de segundos al intentar asignar búferes de recepción . Además, generó errores que indicaban que se había perdido el tiempo de espera, lo que probablemente se debió a que los paquetes no llegaban al software de control.
Solo para resaltar la extrañeza: la PC A es la PC que envía paquetes UDP en este caso.
Editar en respuesta a EJP: esta es la configuración de trabajo (ahora). Comenzó como:
forever at 2000Hz { send current command; receive response; }Pero cuando se recibió la respuesta (bloqueo) se pasó el plazo. Por lo tanto, la verificación de disponibilidad.
Otra cosa que se intentó fue recibir en un hilo separado:
// thread A forever at 2000Hz { send current command; } // thread B forever { receive response; }Que muestra el mismo comportamiento que la primera versión.
La solución fue configurar el zócalo en la PC B en modo sin bloqueo. Una línea y todos los problemas desaparecieron.
Estoy bastante seguro de que incluso en el modo de bloqueo se cumplió el plazo. No debería haber diferencia de rendimiento entre el modo de bloqueo y el de no bloqueo cuando solo hay un zócalo involucrado. Incluso si verificar el socket para obtener datos disponibles toma algunos microsegundos más que en el modo sin bloqueo, no debería hacer una diferencia cuando se cumple con precisión la fecha límite general.
Ahora... ¿qué está pasando aquí?
Si leo tu código correctamente y me refiero a este código:
forever at 2000Hz { send current command; receive response; }Examine la diferencia entre el zócalo de bloqueo y el de no bloqueo. Con el socket de bloqueo, envía el comando actual y luego se queda atascado esperando la respuesta. En este momento, supongo que ya no alcanza la meta de 2kHz.
Ahora, en el socket sin bloqueo, envía el comando actual, intenta recibir lo que esté en los búferes de recepción, pero si no hay nada allí, regresa inmediatamente y continúa con tu ciclo de envío ajustado de 2 kHz. Esto me explica por qué su sistema de control industrial funciona bien en código sin bloqueo.