У нас есть приложение C ++, которое общается с сервером.Он отправляет ему два сообщения, и сервер отвечает на каждое сообщение другим сообщением.Мы используем Boost, но Boost Socket - все приложение - barfs, когда мы пытаемся закрыть сокет.
Вот общее представление о том, что мы делаем:
- закодировать сообщение (изменить его в строку)
- открыть сокет
- отправить сообщение
- проверить отправленные байты
- проверить сообщение возврата
- выключение и закрытие сокета
Поскольку мы отправляем два сообщения, мы делаем это в цикле (очевидно, всего две итерации).
Мы точно знаем, где ошибкапотому что, если мы удалим эту строку, она работает нормально.Это на шаге 5. К сожалению, это довольно важный шаг.Мы не можем найти то, что делаем неправильно, как это исправить.
Вот код:
bool ReallyImportantService::sendMessages( int messageNum ) {
// ...some error-checking here...
bool successCode = false;
for( int i = 0; i < 2; ++i ) {
successCode = false;
unique_ptr<boost::asio::ip::tcp::socket> theSocket = connect();
if( theSocket == nullptr ) {
theLogger->error( "Could not create socket, could not send input messageNum to service" );
return successCode;
}
string message = encodeMessage( messageNum );
// send the message
boost::system::error_code error;
size_t bytesSent = boost::asio::write(*theSocket,
boost::asio::buffer(message),
boost::asio::transfer_all(), error);
// inspect the result
if( !messageNumSendSuccessful(message.length(), bytesSent) ) {
return successCode;
}
// Get the response message
string response;
boost::system::error_code e;
boost::asio::streambuf buffer;
// this is step #5 above, the line that kills it. But it responds with no errors
boost::asio::read_until(*theSocket, buffer, "\0", e);
if( e.value() == boost::system::errc::success ) {
istream str(&buffer);
getline(str, response);
// validate response
successCode = messageAckIsValid( response, messageNum );
}
else {
theLogger->error( "Got erroneous response from server when sending messageNum" );
}
// close it all up
boost::system::error_code eShut;
theSocket->shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, eShut);
// We never get an error code here, all clean
try {
boost::system::error_code ec;
// This is where it all goes belly-up. It doesn't throw an exception, doesn't return an
// error-code. Stepping through, we can see the call stack shows a Segmentation fault,
// but we don't know what could be causing this.
theSocket->close( ec );
}
catch(boost::system::system_error& se) {
theLogger->error( "sendMessages() barfed on close! " + string(se.what()) );
}
catch( ... ) {
theLogger->error( "sendMessages() barfed on close! " );
}
}
return successCode;
}
string ReallyImportantService::encodeMessage( int messageNum ) {
// Encode the message
stringstream ss;
ss << "^FINE=";
ss << to_string(messageNum) << "\n";
string message = ss.str();
theLogger->info( message );
return message;
}
unique_ptr<boost::asio::ip::tcp::socket> ReallyImportantService::connect() {
// Addresses from configuration
string address( server_ip );
string port( server_port );
// Resolve the IP address
boost::asio::io_service ioService;
boost::asio::ip::tcp::resolver resolver(ioService);
boost::asio::ip::tcp::resolver::query query(address, port);
boost::asio::ip::tcp::resolver::iterator ep_iterator = resolver.resolve(query);
// create the socket
unique_ptr<boost::asio::ip::tcp::socket> theSocket = make_unique<boost::asio::ip::tcp::socket>(ioService);
// not sure if this is necessary, but couldn't hurt; we do reuse the IP address the second time around
boost::system::error_code ec;
theSocket->set_option(boost::asio::socket_base::reuse_address(true), ec);
// Connect
try {
boost::asio::connect(*theSocket, ep_iterator);
} catch(const boost::system::system_error &e){
theSocket = nullptr;
theLogger->error( "Exception while attempting to create socket: " + string(e.what()) );
} catch(const exception &e){
theSocket = nullptr;
theLogger->error( "Exception while attempting to create socket: " + string(e.what()) );
}
return theSocket;
}
Вот стек вызовов, который мы получаем, когда он выдает ошибку:
(Suspended : Signal : SIGSEGV:Segmentation fault)
pthread_mutex_lock() at 0x7ffff7bc8c30
boost::asio::detail::posix_mutex::lock() at posix_mutex.hpp:52 0x969072
boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex>::scoped_lock() at scoped_lock.hpp:36 0x980b66
boost::asio::detail::epoll_reactor::free_descriptor_state() at epoll_reactor.ipp:517 0x96c6fa
boost::asio::detail::epoll_reactor::deregister_descriptor() at epoll_reactor.ipp:338 0x96bccc
boost::asio::detail::reactive_socket_service_base::close() at reactive_socket_service_base.ipp:103 0xb920aa
boost::asio::stream_socket_service<boost::asio::ip::tcp>::close() at stream_socket_service.hpp:151 0xb975e0
boost::asio::basic_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >::close() at basic_socket.hpp:339 0xb94f0d
ReallyImportantService::sendMessages() at ReallyImportantService.cc:116 0xb8ce19
<...more frames...>
Мы создали минимальную реализацию, которая просто:
- Создает сокет
- Выключает сокет
- Закрывает сокет
И это прекрасно работает.Мы поместили его в цикл и можем без проблем выполнить десятки итераций.
Мы используем Eclipse CDT и gcc для компиляции.
Есть идеи, что может произойти?