Mám server aplikace, která používá boost ASIO komunikovat s několika klienty. Serverová aplikace běží na Linux serveru a klienti běží na počítačích se systémem Windows.
Současný design je multi-threaded i když tam je jen jeden boost ASIO thead (který běží boost::asio::io_context
). Boost ASIO vlákno je odpovědný pouze pro čtení, psaní, a některé občasné odeslání. Čtení se provádí pomocí boost::asio::async_read
ale kopie výsledné zprávy tak, že se další vlákno může dělat práci zpracování. Psaní se provádí pomocí boost::asio::write
ale zpráva již byla zkopírována a předán boost ASIO závit
Ve většině případů, kdy se klient odpojí boost ASIO vyhodí chybu, vypnul jsem souborům zásuvky a ostatní zásuvky pokračovat v práci. Však pokud klient je Windows desktop má výpadku napájení, zatímco boost::asio::write
je psaní pro ně pak zvýšit nerozpozná problém, a visí v boost::asio::write
. Visí na téměř 20 minut, někdy a server nemůže komunikovat s ostatními klienty během této doby
Z toho, co jsem četl, on-line autoři boost ASIO nemám v úmyslu zavést parametr timeout. Zkoušel jsem nastavení SO_SNDTIMEO na 5 sekund, ale to nemělo žádný vliv na zápis pověsit. Jak nyní, můj nejlepší odhad, jak vyřešit problém je to, aby každý socket jiném vlákně tak, že jeden klient nemůže sundat další klienty. Existují nějaké lepší možnosti než tohle? Jestli mám dát každé zásuvky jeho vlastní vlákno, znamená to, že budu potřebovat boost::asio::io_context
za nit, aby se zabránilo psát pověsit?
Edit: Po shlédnutí komentářů jsem se snažil předělání funkce, která se volá boost::asio::write
s boost::asio::async_write
. Níže jsem nějaký kód, který byl zjednodušen TAK, ale stále ukazuje, jaké celkové změny bylo:
Původně s 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();
});
}
Přepracován s 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();
}
);
});
}
Zámek byl představen v druhé kód zaručit pouze jeden hovor boost::asio::async_write
byl aktivní v době, (jsem si vědom, že existují výkonnější způsoby, jak to udělat, ale je to jednodušší pro testování). Oba tyto kódy mají stejný problém visí boost ASIO, kdy klient má výpadku napájení. Nicméně oni nemají viset v různých způsobů, asynchronní kód neumožňuje pro boost ASIO provádět jiné akce, jen tak dále píše, dokud visí jeden produkuje chybě
Při samostatném experimentu zkoušel jsem nastavení SO_KEEPALIVE
ale to také neřeší problém pověsit