Am un server de aplicație care utilizează boost ASIO pentru a comunica cu mai mulți clienți. Aplicația server ruleaza pe un server Linux și clienții rula pe desktop-ul Windows.
Designul actual este multi-threaded deși există doar un singur boost ASIO thead (care ruleaza boost::asio::io_context
). Boost ASIO subiect este responsabil doar pentru citire, scriere, și unele rare de expediere. Citirea se face cu ajutorul boost::asio::async_read
dar copii care rezultă mesaj, astfel încât un alt thread poate face munca de prelucrare. Scrierea se face cu ajutorul boost::asio::write
dar mesajul a fost deja copiat si dat afara pentru boost ASIO fir
În cele mai multe cazuri, atunci când un client se deconectează boost ASIO aruncă o eroare, am închis asociat socket, și alte prize ține de lucru. Cu toate acestea, dacă un client desktop pentru Windows are o pană de curent în timp ce boost::asio::write
este scris pentru ei, atunci boost nu detecta o problemă și se blochează în boost::asio::write
. Se blochează de aproape 20 de minute, uneori și serverul nu poate comunica cu alți clienți în acest timp
Din ce am citit online, autorii boost ASIO au nici o intenție de a introduce o pauză parametru. Am incercat setarea SO_SNDTIMEO la 5 secunde, dar care nu au nici un efect asupra scrie bloca. Ca de acum bănuiala mea de a rezolva problema este de a oferi fiecare mufa un fir diferit, astfel că un client nu poate să ia pe alți clienți. Există opțiuni mai bune decât asta? Dacă eu dau de fiecare priza firul asta înseamnă că va avea nevoie de un boost::asio::io_context
pe fir pentru a evita scrie stea?
Edit: Dupa ce am vazut comentarii am încercat refacerea funcția pe care o solicită boost::asio::write
cu boost::asio::async_write
. Mai jos am un cod care a fost simplificat pentru ATÂT, dar încă arată ce schimbare totală a fost:
Inițial cu 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();
});
}
Refăcut cu 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();
}
);
});
}
De blocare a fost introdus în cel de-al doilea cod pentru a garanta un singur apel la boost::asio::async_write
a fost activ la un moment dat (sunt conștient de faptul că există mai performante moduri de a face acest lucru, dar acest lucru este mai simplu pentru testare). Ambele coduri au aceeași problemă de agățat boost ASIO atunci când clientul are o pană de curent. Cu toate acestea ei fac atârnă în diferite moduri, codul asincron permite pentru boost ASIO pentru a efectua alte acțiuni, nu mai scrie până la agățat unul produce o eroare
În timpul unui experiment separat am încercat stabilirea SO_KEEPALIVE
dar care, de asemenea nu a rezolvat problema stea