Eu tenho um servidor de aplicativo que usa o boost ASIO para se comunicar com vários clientes. A aplicação de servidor é executado em um servidor Linux e clientes executar em ambientes de trabalho do Windows.
O design atual é multi-threaded embora haja apenas um impulso ASIO thead (que é executado boost::asio::io_context
). O impulso ASIO thread só é responsável para a leitura, a escrita, e alguns raros envio. A leitura é feita usando boost::asio::async_read
mas copia a mensagem resultante, de modo que outro thread pode fazer o trabalho de processamento. A escrita é feita usando boost::asio::write
mas a mensagem já foi copiado e entregue para o impulso ASIO thread
Na maioria das circunstâncias, quando um cliente se desconecta impulso ASIO lança um erro, eu encerrar o associado tomada, e a outra sockets continuar a trabalhar. No entanto, se um cliente da área de trabalho do Windows tem uma falha de energia durante boost::asio::write
é escrever a eles, em seguida, impulsionar não detectar um problema e trava em boost::asio::write
. Ele trava por quase 20 minutos e, por vezes, o servidor não pode se comunicar com outros clientes durante este tempo
Pelo que tenho lido online os autores de impulsionar o ASIO não tenho nenhuma intenção de introduzir um parâmetro de tempo limite. Eu tentei definição SO_SNDTIMEO para 5 segundos, mas que não tem qualquer efeito sobre a escrever travar. A partir de agora meu melhor palpite para resolver o problema é o de dar a cada soquete de um thread diferente, de modo que um cliente não pode derrubar os outros clientes. Existem opções melhores do que este? Se eu não dar a cada um o soquete seu próprio thread isso significa que vai precisar de um boost::asio::io_context
por thread para evitar a gravação de travar?
Edit: Depois de ver os comentários eu tentei refazer a função que chama boost::asio::write
com boost::asio::async_write
. Abaixo eu fiz um código que foi simplificado para ISSO, mas ainda mostra que a variação global foi:
Originalmente com 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();
});
}
Refeito com 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();
}
);
});
}
O bloqueio foi introduzido o segundo código para garantir que apenas uma chamada para boost::asio::async_write
foi ativo por vez (estou ciente de que existem mais alto desempenho maneiras de fazer isso, mas isso é mais simples para teste). Ambos estes códigos têm o mesmo problema de suspensão impulso ASIO quando o cliente tem uma falha de energia. No entanto, eles não reagir de maneiras diferentes, o código assíncrono permite aumentar ASIO para realizar outras ações, apenas não mais escreve, até a de suspensão produz um erro
Durante separado experimento que eu fiz tente definir SO_KEEPALIVE
mas que também não conseguiu resolver o problema de travar