Tengo un subproceso de trabajo que escucha un socket TCP para el tráfico entrante y almacena en búfer los datos recibidos para que acceda el subproceso principal (llamemos a este socket A). Sin embargo, el subproceso de trabajo también tiene que realizar algunas operaciones regulares (digamos, una vez por segundo), incluso si no ingresan datos. Por lo tanto, uso select()
con un tiempo de espera, para que no tenga que seguir sondeando. (Tenga en cuenta que llamar receive()
en un socket sin bloqueo y luego dormir por un segundo no es bueno: los datos entrantes deben estar disponibles de inmediato para el subproceso principal, aunque es posible que el subproceso principal no siempre pueda procesarlos de inmediato, de ahí la necesidad de almacenamiento en búfer. )
Ahora, también necesito poder indicarle al subproceso de trabajo que haga otras cosas de inmediato; del subproceso principal, necesito hacer que el subproceso de trabajo select()
volver enseguida. Por ahora, he resuelto esto de la siguiente manera (enfoque básicamente adoptado de aquí y aquí):
Al iniciar el programa, el subproceso de trabajo crea para este propósito un socket adicional del tipo de datagrama (UDP) y lo vincula a algún puerto aleatorio (llamemos a este socket B). Asimismo, el hilo principal crea un socket de datagrama para enviar. En su llamado a select()
el subproceso de trabajo ahora enumera ambos A y B en el fd_set
. Cuando el subproceso principal necesita enviar una señal, sendto()
‘un par de bytes al puerto correspondiente en localhost
. De vuelta en el subproceso de trabajo, si B permanece en el fd_set
después select()
regresa, entonces recvfrom()
se llama y los bytes recibidos simplemente se ignoran.
Esto parece funcionar muy bien, pero no puedo decir que me guste la solución, principalmente porque requiere vincular un puerto adicional para By también porque agrega varias llamadas API de socket adicionales que pueden fallar, supongo, y realmente no tengo ganas de averiguar la acción adecuada para cada uno de los casos.
Creo que idealmente, me gustaría llamar a alguna función que tome A como entrada, y no hace nada excepto hace select()
volver enseguida. Sin embargo, no conozco esa función. (Supongo que podría por ejemplo shutdown()
el zócalo, pero los efectos secundarios no son realmente aceptables 🙂
Si esto no es posible, la segunda mejor opción sería crear un B que es mucho más tonto que un socket UDP real, y realmente no requiere asignar recursos limitados (más allá de una cantidad razonable de memoria). supongo Conectores de dominio Unix haría exactamente esto, pero: la solución no debería ser mucho menos multiplataforma que la que tengo actualmente, aunque una cantidad moderada de #ifdef
las cosas estan bien (Me estoy dirigiendo principalmente a Windows y Linux, y por cierto, escribo C++).
No sugiera refactorizar para deshacerse de los dos hilos separados. Este diseño es necesario porque el subproceso principal puede estar bloqueado durante períodos prolongados (por ejemplo, al hacer algunos cálculos intensivos, y no puedo comenzar a llamar periódicamente receive()
desde el ciclo de cálculo más interno), y mientras tanto, alguien necesita almacenar en búfer los datos entrantes (y debido a razones que escapan a mi control, no puede ser el remitente).
Ahora que estaba escribiendo esto, me di cuenta de que alguien definitivamente va a responder simplemente “Boost.asio“, así que acabo de verlo por primera vez… Sin embargo, no pude encontrar una solución obvia. Tenga en cuenta que tampoco puedo (fácilmente) afectar cómo el socket A se crea, pero debería poder dejar que otros objetos lo envuelvan, si es necesario.