Существует ли устройство boost::iostreams (двунаправленное) для блокировки TCP-соединения boost::asio?

Я изучаю библиотеки С++ для портативных, блокирующих доступ ввода-вывода к файловой системе и сети. Похоже, что boost::filesystem, boost::iostreams и boost::asio будут выполнять эту работу втроем.

Чтобы было ясно, в настоящее время меня не интересуют асинхронные аспекты boost::asio; Я просто хочу портативный, блокирующий интерфейс к сети.

Копаясь, я вижу, что boost::iostreams имеет понятие устройств, каждое из которых имеет связанный концепция режима. Двунаправленный режим кажется специально адаптированным для потокового доступа к полнодуплексному TCP-соединению. Потрясающий.

boost::iostreams, похоже, не предлагает поддержку для фактического открытия TCP-соединений (в отличие от локальной файловой системы). Это нормально, конечно, boost::asio позволит мне открыть соединение, соответствующим образом смоделировать его как двунаправленное Device и обернуть его в boost::iostreams::stream.

..кроме этого не будет? Я вижу boost::asio::ip::tcp::iostream, который заменит boost::iostreams::stream, но, по-видимому, не будет действовать как Device.

Я понимаю, что tcp::iostream будет действовать аналогично, но я все же предпочитаю учиться и кодировать только один интерфейс, а не два. В частности, работа с двумя режимами обработки ошибок и иерархиями исключений не очень приятна.

Итак, вопрос: я слепой? Возможно, существует адаптер между двумя библиотеками, который я пропустил. Или, может быть, кто-то уже выпустил такой адаптер в качестве стороннего компонента, который я мог бы забросить?


person phs    schedule 18.08.2012    source источник


Ответы (1)


Я не знаю о прямом отображении. Однако, если вам интересно, написать такое устройство довольно просто. Эта версия выдает boost::system::system_error для ошибок, отличных от EOF, но вы можете сделать что-то еще.

#include <iosfwd>

#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/iostreams/categories.hpp>
#include <boost/system/system_error.hpp>


class asio_stream_device
{
public:
    typedef char char_type;
    typedef boost::iostreams::bidirectional_device_tag category;

    explicit asio_stream_device(boost::asio::ip::tcp::socket& sock) : socket_(sock)
    {

    }

    std::streamsize read(char* s, std::streamsize n)
    {
        // Read up to n characters from the underlying data source
        // into the buffer s, returning the number of characters
        // read; return -1 to indicate EOF
        boost::system::error_code ec;

        std::size_t rval = socket_.read_some(boost::asio::buffer(s, n), ec);
        if (!ec)
        {
            return rval;
        }
        else if (ec == boost::asio::error::eof)
        {
            return -1;
        }
        else
        {
            throw boost::system::system_error(ec,"read_some");
        }

    }


    std::streamsize write(const char* s, std::streamsize n)
    {
        // Write up to n characters to the underlying
        // data sink into the buffer s, returning the
        // number of characters written

        boost::system::error_code ec;
        std::size_t rval = socket_.write_some(boost::asio::buffer(s, n), ec);
        if (!ec)
        {
            return rval;
        }
        else if (ec == boost::asio::error::eof)
        {
            return -1;
        }
        else
        {
            throw boost::system::system_error(ec,"write_some");
        }

    }



private:

    boost::asio::ip::tcp::socket& socket_;

};

По сути, откройте/подключите сокет как обычно, а затем передайте его конструктору. Пример просто читает и выводит на экран.

void test
{
   namespace asio = boost::asio;
   namespace io = boost::iostreams;

   asio::io_service service;
   asio::ip::tcp::socket socket(service);


   asio::ip::tcp::endpoint remote -  ...; ////

   socket.connect(remote);

   io::stream<asio_stream_device> str(socket);

   std::string line;

   while (std::getline(str, line)) {
    std::cout << line << std::endl;
   }
}
person Dave S    schedule 19.08.2012
comment
Я согласен, что это выглядит просто на первый взгляд, но я надеялся избежать этого. В частности, я боюсь, что правильное сопоставление ошибок asio с ошибками iostream (то есть минимизация неожиданностей на разных платформах) может быть довольно сложным. Может быть, я слишком много думаю об этом. - person phs; 19.08.2012
comment
@phs: Ну, насколько я могу судить, устройство boost::iostreams действительно имеет только один интерфейс для сообщения о проблеме, и это происходит путем возврата '-1' из его чтения/записи, что устанавливает eofbit для ошибок чтения и badbit | failbit для записи. неудачи. Поэтому, если вы хотите, вы можете просто сопоставить ВСЕ ошибки asio с кодом «-1» и использовать его вместо этого. - person Dave S; 19.08.2012
comment
На самом деле, они упоминают предпочтение исключений и перечисляют некоторые конкретные семантики, которые они стараются соблюдать: boost.org/doc/libs/1_50_0/libs/iostreams/doc/guide/ . - person phs; 19.08.2012
comment
@фс. Интересно, я этого не читал. В C++11 std::ios_base::failure теперь происходит от std::system_error, поэтому вы просто используете код ошибки ASIO с ios_base::failure (как только ASIO использует код ошибки C++11, а не boost). В противном случае я бы, вероятно, рекомендовал создать новое исключение, которое в основном такое же, как boost::system::system_error, за исключением производного от ios_base::failure. - person Dave S; 19.08.2012
comment
Да, новый маршрут исключения — это то, на что я только что смотрел. К счастью, asio перечисляет все свои коды ошибок, поэтому я мог хотя бы попытаться выполнить сопоставление. Если iostreams на самом деле не реагирует на свои собственные исключения каким-либо особым образом, то сопоставление может быть простым (т. е. одно исключение-оболочка). Тем не менее, жаль, что это уже где-то не сделано. - person phs; 19.08.2012