2017-01-17 7 views
3

Я использую boost::asio::read_until для чтения из гнезда до получения "<EOF>". Тем не менее, кто-то может отправить миллиарды и миллиарды байт до тех пор, пока система не выйдет из ОЗУ и не будет закрыта.Можете ли вы установить байтовый предел для read_until для Asio?

Чтобы избежать этого, я хочу установить предел read_until. Как «читать до "<EOF>" или до достижения 10 МБ».

Есть ли простое решение для использования с read_until, или мне нужно переключиться на read и вручную завершить чтение, когда получено "<EOF>"?

ответ

5

@sehe имеет отличный ответ, который позволяет останавливаться на EOF или когда считывается определенное количество байтов. Мои версии немного сложнее, но дополнительно позволяют останавливаться на любом разделителе.


Вы можете construct ваше подталкивание :: ASIO :: streambuf с максимальным размером аргументом:

Конструктор basic_streambuf принимает size_t аргумент, указывающий максимум суммы размеров входного последовательности и выходной последовательности. Во время жизни объекта basic_streambuf, следующий инвариант имеет место

size() <= max_size()

Любая функция-член, которая, в случае успеха, привести инвариант нарушаться брошу исключение класса станд :: length_error.

Или вы могли бы использовать эту перегрузку:

template< 
    typename SyncReadStream, 
    typename Allocator, 
    typename MatchCondition> 
std::size_t read_until(
    SyncReadStream & s, 
    boost::asio::basic_streambuf<Allocator> & b, 
    MatchCondition match_condition, 
    boost::system::error_code & ec, 
    typename enable_if< is_match_condition<MatchCondition>::value >::type * = 0); 

где функция условия матча выглядит примерно так:

using iterator = buffers_iterator<basic_streambuf<Allocator>::const_buffers_type>; 
/** 
\brief Make read_until stop when either: 
    * the stop character was found 
    * more than 100MB have been read 
*/ 
pair<iterator, bool> match_condition(iterator begin, iterator end) { 
    // to much data? 
    if(end - begin > 100*1024*1024) { 
     return std::make_pair(begin, true); 
    } 
    // try and find stop character 
    for(auto i = begin; i < end; i++) { 
     auto c = i.rdbuf()->sgetc(); 
     if(c == STOP_CHARACTER) { 
     return std::make_pair(i, true); 
     } 
    } 

    return std::make_pair(begin, false); 
} 

(Making этой работа с несколькими символами разделителями в оставляются в качестве упражнение для читателя)

+1

Это хорошо, но очень сложно, IYAM (см. Мой ответ) – sehe

+0

@если вы правы, я немного его отредактировал. Я использовал его для проверки «\ r \ n», поэтому я думал, что OP означает фактические буквы «<», «E», «O», «F», «>» и написал мой ответ с учетом этого. – user45891

+0

На самом деле , это правильный способ прочитать вопрос. Не беспокойтесь, в этом случае это лучший ответ.+1 – sehe

5

Просто используйте transfer_exactly, который будет также остановка в EOF или буфер заполнен:

Live On Coliru

#include <boost/asio.hpp> 
#include <iostream> 
using namespace boost::asio; 
using namespace ip; 

int main() { 
    boost::system::error_code ec; 

    io_service svc; 
    tcp::socket s(svc); 
    s.connect(tcp::endpoint(address_v4::loopback(), 6767)); 

    streambuf sb; 
    auto transferred = read(s, sb, transfer_exactly(10u<<20), ec); 

    std::cerr << "read " << transferred << " till " << ec.message() << "\n"; 
} 
0

Im устанавливая тот же предел, используя streambuf предельному. Пример кода (с использованием сопрограмм):

try { 
    boost::asio::streambuf req_buf(100 * 1024); //Limit memory for headers 
    boost::asio::async_read_until(*sock, req_buf, "\r\n\r\n", yield); 
}catch(boost::system::system_error &e) { 
    if(e.code() == boost::asio::error::make_error_code(boost::asio::error::misc_errors::not_found)) 
     log("HTTP Connection close: cannot find headers end"); 
    //.. 
} 

Thats it. Вы можете проверить код ошибки в обратном вызове чтения и получить конкретную ошибку, если элемент поиска не был найден в буфере чтения.

 Смежные вопросы

  • Нет связанных вопросов^_^