Ho una applicazione server che utilizza la spinta ASIO per comunicare con i diversi clienti. L'applicazione server gira su un server Linux e client eseguire sul desktop di Windows.
Il design attuale è multi-threaded anche se c'è solo una spinta ASIO thead (che corre boost::asio::io_context
). La spinta ASIO thread è responsabile solo per la lettura, la scrittura, e alcuni rari spedizione. La lettura viene fatta utilizzando boost::asio::async_read
ma copia il messaggio risultante in modo che un altro thread può fare il lavoro di elaborazione. La scrittura è fatta utilizzando boost::asio::write
ma il messaggio è già stato copiato e ceduto alla spinta ASIO thread
Nella maggior parte dei casi quando un client si disconnette la spinta ASIO genera un errore, chiudo il socket associato, e le altre prese di continuare a lavorare. Tuttavia, se un client desktop di Windows è un'interruzione dell'alimentazione durante boost::asio::write
la scrittura, quindi, boost non rileva un problema e che si blocca in boost::asio::write
. Si blocca per quasi 20 minuti e a volte il server non è in grado di comunicare con gli altri clienti durante questo periodo di tempo
Da quello che ho letto online gli autori di spinta ASIO non hanno alcuna intenzione di introdurre un parametro di timeout. Ho provato impostazione SO_SNDTIMEO per 5 secondi, ma che non hanno alcun effetto sulla scrittura appendere. Ora come ora la mia ipotesi migliore per risolvere il problema è quello di dare ad ogni presa un thread diverso, in modo che un client non può prendere gli altri clienti. Ci sono opzioni migliori di questo? Se mi faccio dare ogni presa proprio thread vuol dire che ho bisogno di un boost::asio::io_context
per thread per evitare la cancellazione del blocco?
Edit: Dopo aver visto i commenti che ho provato a rifare la funzione che chiama boost::asio::write
con boost::asio::async_write
. Sotto ho un po ' di codice che è stato semplificato per COSÌ tanto, ma mostra ancora che la variazione complessiva è stata:
Originariamente con boost::asio::write
:
inline void MessagingServer::writeMessage(
GuiSession* const a_guiSession,
const PB::Message& a_msg
) {
boost::asio::dispatch(m_guiIoIoContext, [this, a_guiSession, a_msg]() {
// I removed code that writes a_msg's bytes into m_guiIoWriteBuf
// and sets totalSize to simplify for SO
boost::system::error_code error;
boost::asio::write(a_guiSession->m_guiIoGsSocket, boost::asio::buffer(m_guiIoWriteBuf, totalSize), error);
if (UNLIKELY(error))
ERRLOG << a_guiSession->m_gsSessionId << " write failed: " << error.message();
});
}
Rifatto con boost::asio::async_write
:
inline void MessagingServer::writeMessage(
GuiSession* const a_guiSession,
const PB::Message& a_msg
) {
a_guiSession->m_tempMutex.lock();
boost::asio::dispatch(m_guiIoIoContext, [this, a_guiSession, a_msg]() {
// I removed code that writes a_msg's bytes into m_guiIoWriteBuf
// and sets totalSize to simplify for SO
boost::asio::async_write(
a_guiSession->m_guiIoGsSocket,
boost::asio::buffer(m_guiIoWriteBuf, totalSize),
[this, a_guiSession](const boost::system::error_code& a_error, std::size_t) {
if (UNLIKELY(a_error))
ERRLOG << a_guiSession->m_gsSessionId << " write failed: " << a_error.message();
a_guiSession->m_tempMutex.unlock();
}
);
});
}
Il blocco è stato introdotto nel secondo codice di garantire solo una chiamata a boost::asio::async_write
fu attivo alla volta (sono consapevole che ci sono più performanti modi per fare questo, ma questo è più semplice per il test). Entrambi questi codici hanno lo stesso problema di appendere la spinta ASIO quando il client ha un guasto di alimentazione. Tuttavia essi appendere in modi diversi, il codice asincrono non consente, per la spinta ASIO per eseguire altre azioni, solo che non scrive ancora fino pensile produce un errore
Durante un esperimento separato l'ho fatto provare a impostare SO_KEEPALIVE
ma che anche non risolvere il blocco problema