Как асинхронно читать в std :: string, используя Boost :: asio?

== тесты для ссылочного равенства (независимо от того, являются ли они одним и тем же объектом).

.equals() тесты для равенства значений (независимо от того, являются ли они логически «равными»).

Objects.equals () проверяет наличие null перед вызовом .equals(), поэтому вам не нужно (доступно с JDK7, также доступным в Guava ).

String.contentEquals () сравнивает содержимое String с содержимым любого CharSequence (доступно с Java 1.5).

Следовательно, если вы хотите проверить, имеет ли две строки одно и то же значение, вы, вероятно, захотите использовать Objects.equals().

// These two have the same value
new String("test").equals("test") // --> true 

// ... but they are not the same object
new String("test") == "test" // --> false 

// ... neither are these
new String("test") == new String("test") // --> false 

// ... but these are because literals are interned by 
// the compiler and thus refer to the same object
"test" == "test" // --> true 

// ... string literals are concatenated by the compiler
// and the results are interned.
"test" == "te" + "st" // --> true

// ... but you should really just call Objects.equals()
Objects.equals("test", new String("test")) // --> true
Objects.equals(null, "test") // --> false
Objects.equals(null, null) // --> true

Вы почти всегда хотите использовать Objects.equals(). В редкой ситуации, когда вы знаете, что имеете дело с интернированными строками, вы можете использовать ==.

Из JLS 3.10. 5. Строковые литералы :

Кроме того, строковый литерал всегда ссылается на тот же экземпляр класса String. Это связано с тем, что строковые литералы, или, в более общем смысле, строки, которые являются значениями константных выражений ( §15.28 ), «интернированы», чтобы обмениваться уникальными экземплярами, используя метод String.intern.

blockquote>

. Подобные примеры также можно найти в JLS 3.10.5-1 .

13
задан NmdMystery 10 January 2014 в 20:44
поделиться

1 ответ

В документации Boost.Asio говорится:

Объект буфера представляет собой непрерывную область памяти в виде кортежа, состоящего из указателя и размера в байтах. Кортеж вида {void*, size_t} указывает на изменяемую (модифицируемую) область памяти.

Это означает, что для того, чтобы вызов async_read записал данные в буфер, он должен быть (в базовом объекте буфера) непрерывным блоком памяти. Кроме того, объект буфера должен иметь возможность записи в этот блок памяти.

std::string не позволяет произвольную запись в свой буфер, поэтому async_read не может записывать куски памяти в буфер строки (обратите внимание, что std:: string does предоставляет вызывающей стороне доступ только для чтения к базовому буферу через метод data(), который гарантирует, что возвращаемый указатель будет действителен до следующего вызова неконстантной функции-члена. По этой причине Asio может легко создать const_buffer, обернув std::string, и вы можете использовать его с async_write).

В документации Asio есть пример кода для простой программы "чат" (см. http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat), в котором есть хороший метод преодоления этой проблемы. По сути, вам нужно, чтобы отправляющий TCP сначала передал размер сообщения в своеобразном "заголовке", а ваш обработчик чтения должен интерпретировать заголовок, чтобы выделить буфер фиксированного размера, подходящий для чтения фактических данных.

Что касается необходимости использования shared_from_this() в async_read и async_write, то причина в том, что это гарантирует, что метод, обернутый boost::bind, всегда будет ссылаться на живой объект. Рассмотрим следующую ситуацию:

  1. Ваш метод handle_accept вызывает async_read и отправляет обработчик "в реактор" - по сути, вы попросили io_service вызвать Connection::handle_user_read, когда он закончит чтение данных из сокета. io_service сохраняет этот функтор и продолжает свой цикл, ожидая завершения операции асинхронного чтения.
  2. После вашего вызова async_read объект Connection по какой-то причине (завершение программы, состояние ошибки и т.д.) деаллоцируется. )
  3. Предположим, что io_service теперь определяет, что асинхронное чтение завершено, после того, как Connection объект был деаллоцирован, но до того, как io_service был уничтожен (это может произойти, например, если io_service: :run выполняется в отдельном потоке, что является типичным). Теперь io_service пытается вызвать обработчик, и у него есть недопустимая ссылка на объект Connection.

Решением является выделение Connection через shared_ptr и использование shared_from_this() вместо this при отправке обработчика "в реактор". это позволяет io_service хранить общую ссылку на объект, а shared_ptr гарантирует, что он не будет деаллоцирован, пока не истечет срок действия последней ссылки.

Итак, ваш код должен выглядеть примерно так:

class Connection : public boost::enable_shared_from_this<Connection>
{
public:

    Connection(tcp::acceptor &acceptor) :
        acceptor_(acceptor), 
        socket_(acceptor.get_io_service(), tcp::v4())
    { }

    void start()
    {
        acceptor_.get_io_service().post(
            boost::bind(&Connection::start_accept, shared_from_this()));
    }

private:

    void start_accept()
    {
        acceptor_.async_accept(socket_, 
            boost::bind(&Connection::handle_accept, shared_from_this(), 
            placeholders::error));
    }

    void handle_accept(const boost::system::error_code& err)
    {
        if (err)
        {
            disconnect();
        }
        else
        {
            async_read(socket_, boost::asio::buffer(user_),
                boost::bind(&Connection::handle_user_read, shared_from_this(),
                placeholders::error, placeholders::bytes_transferred));
        }
    }
    //...
};

Обратите внимание, что теперь вы должны убедиться, что каждый объект Connection выделен через shared_ptr, например:

boost::shared_ptr<Connection> new_conn(new Connection(...));

Надеюсь, это поможет!

31
ответ дан 1 December 2019 в 17:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: