Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> [boost asio] ошибка при выполнени 
:(
    Опции темы
cupper
Дата 28.12.2010, 11:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: 1
Всего: 1



Пытаюсь реализовать следующее. Есть класс, он умеет сериализоваться. Нужно передать его с клиента на сервер, на сервере с ним что то сделать, и вернуть назад.

Логика работы следующая. 
есть класс Connection, с реализованными шаблонными функция для отправки и принятия данных.
Код

#ifndef __CONNECTION_HPP__
#define __CONNECTION_HPP__

#include <boost/asio.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/tuple/tuple.hpp>

#include <iomanip>
#include <string>
#include <sstream>
#include <vector>


class Connection{
public:
    Connection(boost::asio::io_service& io_service)
        : socket_(io_service)
    {
    }
    
    boost::asio::ip::tcp::socket& socket()
    {
        return socket_;
    }
    
    template<typename T, typename Handler>
    void async_write(const T* data, Handler handler)
    {
        // сериализуем данные
        DataToString(outbound_data_, data);
                
        // формируем хедер, заданой длины, содержащий размер отправляемых данных
        std::ostringstream header_stream;
        header_stream << std::setw(header_length_)
            << std::hex << outbound_data_.size();
            
        if(!header_stream || header_stream.str().size() != header_length_){
        // header_stream пустой или его длина не равняется
        // обязательной длине заголовка
            boost::system::error_code error(boost::asio::error::invalid_argument);
            socket_.io_service().post(boost::bind(handler, error));
            return;
        }
        // Сформировываем строку для заголовка
        outbound_header_ = header_stream.str();
        
        std::vector<boost::asio::const_buffer> buffers;
        buffers.push_back(boost::asio::buffer(outbound_header_));
        buffers.push_back(boost::asio::buffer(outbound_data_));
        boost::asio::async_write(socket_, buffers, handler);    
    }
    
    template<typename T, typename Handler>
    void async_read(T* data, Handler handler)
    {
        void (Connection::*f)(
            const boost::system::error_code&,
            T*, boost::tuple<Handler>) 
            = &Connection::handle_read_header<T, Handler>;
        
        boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_),
            boost::bind(f, 
                this, boost::asio::placeholders::error, data,
                boost::make_tuple(handler)));
    }
    
    template<typename T, typename Handler>
    void handle_read_header(const boost::system::error_code& e,
        T* data, boost::tuple<Handler> handler)
    {
        if(e){
            boost::get<0>(handler)(e);
        }
        else{
            std::istringstream is(std::string(inbound_header_, header_length_));
            std::size_t inbound_data_size = 0;
            if(!(is >> std::hex >> inbound_data_size)){
                boost::system::error_code error(
                    boost::asio::error::invalid_argument);
                boost::get<0>(handler)(error);
                return;
            }
            
            inbound_data_.resize(inbound_data_size);
            void (Connection::*f)(
                const boost::system::error_code&,
                T*, boost::tuple<Handler>)
                = &Connection::handle_read_data<T, Handler>;
            
            boost::asio::async_read(socket_, boost::asio::buffer(inbound_data_),
                boost::bind(f, this, 
                    boost::asio::placeholders::error, data, handler));
        }
    }
    
    template<typename T, typename Handler>
    void handle_read_data(const boost::system::error_code& e, 
        T* data, boost::tuple<Handler> handler)
    {
        if(e)
        {
            boost::get<0>(handler)(e);
        }
        else
        {
            try
            {
                std::string archive_data(&inbound_data_[0], inbound_data_.size());
                StringToData(archive_data, data);
            }
            catch(std::exception& e)
            {
                boost::system::error_code error(
                    boost::asio::error::invalid_argument);
                boost::get<0>(handler)(error);
                return;
            }
            
            boost::get<0>(handler)(e);
        }
    }
            

private:
    template<typename T>
    void DataToString(std::string& outstr, T* data)
    {
        std::ostringstream archive_stream;
        boost::archive::xml_oarchive archive(archive_stream);
        archive << boost::serialization::make_nvp("data",*data);
        // переводим в строку
        outstr = archive_stream.str();
    }
    template<typename T>
    void StringToData(const std::string& instr, T* data)
    {
        std::istringstream archive_stream(instr);
        boost::archive::xml_iarchive archive(archive_stream);
        archive >> boost::serialization::make_nvp("data",*data);
    }
    boost::asio::ip::tcp::socket socket_;
    
    // Длина заголовка, содержащий длину принимаемого сообщения
    const static size_t header_length_ = 8;
    
    std::string outbound_header_;
    std::string outbound_data_;
    
    char inbound_header_[header_length_];
    std::vector<char> inbound_data_;
};

typedef boost::shared_ptr<Connection> Connection_ptr;

#endif

класс взят из тутториала и допилен.

Есть класс client который. Единственная особенность которую можно унего выделить это то что класс содержит два указателя (один обычный, указывающий на отправляемые данные, один умных для принимаемых данных, что бы не следить за их корректным удалением)
Код

#ifndef __CLIENT_HPP__
#define __CLIENT_HPP__

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/vector.hpp>
#include <vector>

#include "ListPerson.h"
#include "connection.hpp" // Must come before boost/serialization headers.

class Client
{
public:
  /// Constructor starts the asynchronous connect operation.
  Client(boost::asio::io_service& io_service,
      const std::string& host, const std::string& service, epam::ListPerson& data)
    : connection_(io_service)
  {
    sendData_ = &data;
    recvData_.reset(new epam::ListPerson());
    // Resolve the host name into an IP address.
    boost::asio::ip::tcp::resolver resolver(io_service);
    boost::asio::ip::tcp::resolver::query query(host, service);
    boost::asio::ip::tcp::resolver::iterator endpoint_iterator =
      resolver.resolve(query);
    boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;

    // Начинаем процедуру подключения к серверу
    connection_.socket().async_connect(endpoint,
        boost::bind(&Client::handle_connect, this,
          boost::asio::placeholders::error, ++endpoint_iterator));
  }

  // Верунть принятые от сервера данные
  boost::shared_ptr<epam::ListPerson> GetRecvData() const
  {
    return recvData_;
  }

private:
  void handle_connect(const boost::system::error_code& e,
      boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
  {
    if (!e)
    {
      // Соединение прошло успешно
      // Отправляем данные
        connection_.async_write(sendData_,
          boost::bind(&Client::handle_write, this, boost::asio::placeholders::error));
    }
    else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator())
    {
      // Иначе пробудем подлкючиться к следующей конечной точке
      connection_.socket().close();
      boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
      connection_.socket().async_connect(endpoint,
          boost::bind(&Client::handle_connect, this,
            boost::asio::placeholders::error, ++endpoint_iterator));
    }
    else
    {
      std::cerr << e.message() << std::endl;
    }
  }

  void handle_write(const boost::system::error_code& e)
  {
    if (!e)
    {
      // Данные отправленны
      // Принимает ответ
      connection_.async_read(recvData_.get(),
          boost::bind(&Client::handle_read, this,
            boost::asio::placeholders::error));
    }
    else
    {
      std::cerr << e.message() << std::endl;
    }
  }

  /// Handle completion of a read operation.
  void handle_read(const boost::system::error_code& e)
  {
    if (!e)
    {
      std::cout << recvData_.get();
    }
    else
    {
      // An error occurred.
      std::cerr << e.message() << std::endl;
    }

  }

private:
  /// The connection to the server.
  Connection connection_;

  /// The data received from the server.
  epam::ListPerson* sendData_;
  boost::shared_ptr<epam::ListPerson> recvData_;
};

#endif // __CLIENT_HPP__


есть серверный класс, который содержит просто один умный указатель (он указывает на пустой объект который моздается в конструкторе, в него производится десереализация данных, их модификация и отправка обратно клиенту.
Код

#ifndef __SERVER_HPP__
#define __SERVER_HPP__

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/serialization/vector.hpp>

#include <iostream>
#include <vector>

#include "connection.hpp"
#include "ListPerson.h"

class Server
{
public:
  Server(boost::asio::io_service& io_service, unsigned short port)
    : acceptor_(io_service,
        boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
  {
      data_.reset(new epam::ListPerson());
    // Start an accept operation for a new connection.
    Connection_ptr new_conn(new Connection(acceptor_.io_service()));

    acceptor_.async_accept(new_conn->socket(),
        boost::bind(&Server::handle_accept, this,
          boost::asio::placeholders::error, new_conn));
          
  }

  void handle_accept(const boost::system::error_code& e, Connection_ptr conn)
  {
    if (!e)
    {
      // Соединение активировано
      // Принимаем данные
        
        conn->async_read(data_.get(),
          boost::bind(&Server::handle_read, this,
            boost::asio::placeholders::error, conn));
            
    }
    else
    {
      std::cerr << e.message() << std::endl;
    }
  }

  /// Handle completion of a read operation.
  void handle_read(const boost::system::error_code& e, Connection_ptr conn)
  {
    if (!e)
    {
        // чтото делаем сданными
        std::cout << data_.get() << std::endl;
        data_->Add(boost::shared_ptr<epam::Person>(new epam::Student("Server BSD'ec", epam::Gender::MAN, "1977.07.20", "XX-42", 3.56)));
      // Отправляем ответ
        conn->async_write(data_.get(),
          boost::bind(&Server::handle_write, this,
            boost::asio::placeholders::error));

        // Данные отправлены, запускаем новое ожидание подключения
        std::cout << "\nRestart ACCEPTRO\n";
        Connection_ptr new_conn(new Connection(acceptor_.io_service()));
        acceptor_.async_accept(new_conn->socket(),
            boost::bind(&Server::handle_accept, this,
            boost::asio::placeholders::error, new_conn));
    }
    else
    {
      std::cerr << e.message() << std::endl;
    }
  }

  void handle_write(const boost::system::error_code& e)
  {
    if(!e)
    {
        // Вот СЮДА НЕДОХОДИТ

    }
    else
    {
      std::cerr << e.message() << std::endl;
    }
  }

private:
  boost::asio::ip::tcp::acceptor acceptor_;
  boost::shared_ptr<epam::ListPerson> data_;
};

#endif // __SERVER_HPP__


это все собирается, запускается сервер, запускается клиент. Данные с клиента уходят, на сервере принимаються, к ним добавляется еще один елемент, они отправляются, перезапускается ожидание нового подключения (происходит в функции handle_read) И до функции handle_write на которой полный цикл работы должен завершиться, недоходит. Вылетает ошибка студии об ошибке отлабки, и несодержащая вообше не какой полезной информации. До клиента данные благополучно доходят. Ошибка возникает на стороне сервера. Найти или понять где я не в силах, вообще понятие не приложу в чем проблемма.

user posted image

Возможно ктото предложит более лучьшую реализацию задуманного, которая возможно поможет устранить данную ошибку. Буду рад любой помощи.
PM MAIL   Вверх
cupper
Дата 30.12.2010, 15:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: 1
Всего: 1



после долгих экспериментов было выявлено что если сделать логику такой:

клиент отправляет данные серверу, данный принимает и выводит (и.е. по одному действия) то все корректно работает.

Если же так:

клиент отправляет данные первый раз, сервер принимает и на этом же соединении ожидает еще данных, после отправки клиентом второй порции данных сервер (тех же самых, точно также, но на уже открытом соединении) сервер (точно также получает их, но на уже открытый acceptor) не может их корректно получить, 

ошибка проиходит в connection.hpp
Код

async_read(T* data, Handler handler)
    {
        void (Connection::*f)(
            const boost::system::error_code&,
            T*, boost::tuple<Handler>) 
            = &Connection::handle_read_header<T, Handler>;
        
        boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_),
            boost::bind(f, 
                this, boost::asio::placeholders::error, data,
                boost::make_tuple(handler)));

после выполнение операции цтения и переходе в 
Код
template<typename T, typename Handler>
    void handle_read_header(const boost::system::error_code& e,
        T* data, boost::tuple<Handler> handler)
    {
        if(e){
            boost::get<0>(handler)(e);
        }
        else{
            std::istringstream is(std::string(inbound_header_, header_length_));
            std::size_t inbound_data_size = 0;
            if(!(is >> std::hex >> inbound_data_size)){
                boost::system::error_code error(
                    boost::asio::error::invalid_argument);
                boost::get<0>(handler)(error);
                return;
            }
            
            inbound_data_.resize(inbound_data_size);
            void (Connection::*f)(
                const boost::system::error_code&,
                T*, boost::tuple<Handler>)
                = &Connection::handle_read_data<T, Handler>;
            
            boost::asio::async_read(socket_, boost::asio::buffer(inbound_data_),
                boost::bind(f, this, 
                    boost::asio::placeholders::error, data, handler));
        }
    }



inbound_header_ содержит уже не массив char из 8 символов, а хрен пойми что. Соответственно из за этого хрен пойми что условие в 
Код

if(!(is >> std::hex >> inbound_data_size)){
                boost::system::error_code error(
                    boost::asio::error::invalid_argument);
                boost::get<0>(handler)(error);
                return;
            }


является true и мы выходим с ошибкой :(

Это сообщение отредактировал(а) cupper - 30.12.2010, 17:07
PM MAIL   Вверх
bsa
Дата 31.12.2010, 12:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



cupper, на твоем бы месте, я вместо T*data использовал что-то с управляемым временем жизни, например shared_array. Потому что не исключена ситуация, что хэндл из async_read срабатывает уже после того, как память из-под data будет освобождена.
И вообще, код у тебя ужасный. Может, правда, дело в том, что ты делаешь шаблоны...
PM   Вверх
cupper
Дата 10.1.2011, 01:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: 1
Всего: 1



Цитата(bsa @ 31.12.2010,  12:02)
cupper, на твоем бы месте, я вместо T*data использовал что-то с управляемым временем жизни, например shared_array. Потому что не исключена ситуация, что хэндл из async_read срабатывает уже после того, как память из-под data будет освобождена.
И вообще, код у тебя ужасный. Может, правда, дело в том, что ты делаешь шаблоны...

функции типа async_read приведенные выше это чернорабочие функции класса Connection, он ничего не знает о данных и не следит за ними. работа с данными происходит в классах Server и Client, там как раз реализованы умные указатели и прочее интеллектуальное поведение.


PS. коли код ужасен, дайте пожалуйста пример прекрасного кода для поставленных целей ?

Это сообщение отредактировал(а) cupper - 10.1.2011, 14:48
PM MAIL   Вверх
cupper
Дата 10.1.2011, 18:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: 1
Всего: 1



Хммммм.... а может это быть баг студии или asio под Win? Взял исходники, навоял cmake сборщик, собрал на убунте... работает, ошибок нет, не вылетает. Сервер не падает, несколько раз запускаю клиент данные отправляются и принимаются и не каких ошибок... очень странно...

PS. Прикрепляю проект, в сборке для Visual Studio 2010 и Cmake, кому не лень можете проверить будут ли у вас какие либо ошибки при выполнении проявляться.

Это сообщение отредактировал(а) cupper - 10.1.2011, 19:23

Присоединённый файл ( Кол-во скачиваний: 5 )
Присоединённый файл  for_linux.tar.gz 11,02 Kb
PM MAIL   Вверх
cupper
Дата 10.1.2011, 19:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: 1
Всего: 1



под VS2010 (для обоих необходимо наличие boost желательно последнего).


PS. перенесите пожалуйста тему в раздел "Сети".

Это сообщение отредактировал(а) cupper - 10.1.2011, 19:38

Присоединённый файл ( Кол-во скачиваний: 7 )
Присоединённый файл  for_win_MVS2010.zip 40,10 Kb
PM MAIL   Вверх
cupper
Дата 11.1.2011, 13:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: 1
Всего: 1



ошибка кажется нашлась...

PS. ошибка была в том что терялся объект connection после передачи управления в 
Код

void handle_write(const boost::system::error_code& e)
  {
    if(!e)
    {
        // Вот СЮДА НЕДОХОДИТ

    }
    else
    {
      std::cerr << e.message() << std::endl;
    }
  }

на клиенте такой проблеммы не возникало потому что объект Cnnection существовал в единственно экземпляре и был членом класса Client, тобишь разрушался только когда разрушался сам объект класса Client, тобишь. 

В классе сервер, юзается немного другая логика, там членом класса является объект acceptor, а Connetction создается для каждого нового подключения, и то бишь уничтожался раньше чем нужно было. а это чуть ли не во время работы функции, и при обращении к членам Connection (из его же методов) их как бы уже и не небыло. 

Все это очень приблизительно, и пока не понятно, починил я непосредственно частный случай ошибки, или логику в целом. 

Это сообщение отредактировал(а) cupper - 11.1.2011, 19:43
PM MAIL   Вверх
boostcoder
Дата 11.1.2011, 13:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



поздравляю smile 

PM WWW   Вверх
cupper
Дата 11.1.2011, 20:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: 1
Всего: 1



Цитата(boostcoder @ 11.1.2011,  13:15)
поздравляю smile

вопрос лично тебе (так как ты говорил что ты с asio рабоал)

вот есть код
Код

class Server{
...
void handle_read(const boost::system::error_code& e, Connection_ptr conn, boost::shared_ptr<ListPerson> data_)
  {
    if (!e)
    {
          conn->async_write(data_,
             boost::bind(&Server::handle_write, this,
                boost::asio::placeholders::error));

          // Запускаем ожидание нового подключения
          Connection_ptr new_conn(new Connection(acceptor_.io_service()));
          acceptor_.async_accept(new_conn->socket(),
          boost::bind(&Server::handle_accept, this,
                    boost::asio::placeholders::error, new_conn));
        
    }
    else
    {
      std::cerr << e.message() << std::endl;
    }
  }

  void handle_write(const boost::system::error_code& e)
  {
    if(!e)
    {

    }
    else
    {
      std::cerr << e.message() << std::endl;
    }
  }
...
}

Код

class Connection{
...
template<typename T, typename Handler>
    void async_write(const T data, Handler handler)
    {
                            // подготавливаем данные к отправке
        boost::asio::async_write(socket_, buffers, handler);    
    }
...
}
typedef boost::shared_ptr<Connection> Connection_ptr;

что бросается в глаза ? и есть ли тут грубая ошибка ?.

handle_write, ничего не делает. На нем как бы завершается работа для данного сеанса.
PM MAIL   Вверх
boostcoder
Дата 11.1.2011, 20:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



Цитата(cupper @  11.1.2011,  20:03 Найти цитируемый пост)
что бросается в глаза ?

бардак в структуре smile 

к примеру, класс Server. его основная задача - принимать соединения/логировать, и т.д.. но не общаться с клиентами. зачем ему методы handle_read() и handle_write() - загадка smile 
для общения, у тебя есть класс Connection. но вот что странно - он не наследует enable_shared_from_this smile 
как ты удаляешь объекты Connection ?

Это сообщение отредактировал(а) boostcoder - 11.1.2011, 20:31
PM WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1195 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.