2013-12-06 4 views
3

У меня есть UDP сокет, который будет принимать какие-то пакеты, потенциально разных размеров, и я с этим справиться асинхронно:Переменный размер буфера для приема UDP-пакетов

socket.async_receive_from(boost::asio::buffer(buffer, 65536), senderEndpoint, handler); 

Проблема здесь в том, что для обработки различных размеров У меня большой буфер, что можно было бы решить с помощью буферов с переменным размером.

Насколько я понимаю, при использовании async_receive_from обработчик вызывается только с одним пакетом за раз, поскольку границы пакетов сохраняются в UDP. Итак, есть способ предоставить пустой буфер async_receive_from, что Asio будет расти, чтобы соответствовать размеру пакета?

Также обратите внимание, что я переношу пакеты, поэтому для каждого пакета, проходящего через этот сокет, 4 первых байта являются длиной пакета.

+0

Почему? Просто укажите самый большой буфер, который вам нужен. Метод получения подскажет, сколько байтов было действительно получено. – EJP

+0

Эта программа будет работать на мобильной платформе, а не на ПК, поэтому ее объем памяти должен быть легким. Я думаю, что может быть более эффективным способом, чем выделение большого буфера. – Synxis

+1

Выделение одного большого буфера один раз намного эффективнее, чем выделение большого количества небольших буферов. – EJP

ответ

7

Для более точного ответа здесь приведен подробный и объясненный код.

Во-первых, нам нужно вызвать обработчик приема без заполнения буфера. Это делается с использованием boost::asio::null_buffer() (см. reactor-style operations для получения дополнительной информации, как указано в Tanner).

void UDPConnection::receive() 
{ 
    socket.async_receive(boost::asio::null_buffers(), receive_handler); 
} 

Теперь, когда пакет будет получен, receive_handler будет называться без какого-либо буфера для заполнения.

Теперь для обработчика:

void UDPConnection::handleReceive(const boost::system::error_code& error, unsigned int) 
{ 
    // Standard check: avoid processing anything if operation was canceled 
    // Usually happens when closing the socket 
    if(error == boost::asio::error::operation_aborted) 
     return; 

С socket.available(), мы можем получить число байтов для чтения. Важно: это число не обязательно является размером пакета! Для моих тестов это число всегда превышало размер пакета (даже с 8 kB-пакетами, который был самым большим, на котором мог работать мой компьютер).

// Prepare buffer 
    unsigned int available = socket.available(); 
    unsigned char* buffer = new unsigned char[available]; 

Здесь происходит волшебство: «реальный» получить вызов происходит здесь, и обычно будет быстро, так как это будет просто заполнить буфер. Этот вызов будет отменять только один пакет (даже если во время разговора в соке было более одного). Здесь важно значение возврата, так как может быть заполнена только часть буфера. Пример: доступно = 50, packetSize = 13.

// Fill it 
    boost::asio::ip::udp::endpoint senderEndpoint; 
    boost::system::error_code ec; 
    unsigned int packetSize = socket.receive_from(boost::asio::buffer(buffer, available), senderEndpoint, 0, ec); 

Теперь просто стандартная проверка ошибок/обработка/etc ...

if(ec) 
    { 
     // ... 
    } 

    // Process packet 
    // ... 
    receive(); 
} 
+0

не означает, что вы делаете дополнительную копию данных, потому что теперь Asio нужно хранить данные во внутреннем буфере и только затем скопировать его в буфер? Также вы динамически выделяете буферы для каждого пакета, а не выделяете его один раз, даже если это 8 КБ, он все еще не такой большой. –

+0

Это, наверное, так. Как только я получу время, я, вероятно, улучшу этот ответ (и удаляю утечку памяти, показанную в нем!) – Synxis

1

Вы можете сделать это вручную. my_socket.available() возвращает размер следующего пакета Udp, ожидающего очереди в сокете, который должен быть прочитан. Таким образом, вы можете использовать это, чтобы проверить, насколько большой следующий пакет, соответствующим образом вырасти ваш буфер, а затем получить его. Тем не менее, я согласен с экспертами в том, что это, скорее всего, менее эффективно, чем просто использование максимально возможного размера в качестве буфера. Если, может быть, максимум намного больше, чем средний пакет, и маловероятно, чтобы ваше приложение часто не получало его вообще.

Но это вопрос оптимизации. Ответ на ваш вопрос - available() и буферизацию.

Редактировать: Я знаю, что это меньше, чем идеально в ситуации async, так как available() возвращает только размер следующего пакета udp, если он уже ждет, когда вызывается available().

+3

Для асинхронных ситуаций [операции в стиле реактора] (http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/overview/core/reactor.html) обеспечивают чистое решение для ленивого распределения надлежащим образом размер буфера. –

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