2017-06-11 102 views
1

我正在從串行設備中讀取每個消息必須特別請求的地方。例如。您發送請求並獲得序列化有效負載的響應。如何在連續讀取async_read_until後使用asio緩衝區

每個消息包含在順序包括以下部分:

  1. 前導碼(2個字節, 「$ M」)
  2. HEADER(3個字節,包含有效載荷長度N)
  3. PAYLOAD + CRC(N + 1個字節)

我與ASIO的方法是通過使用asio::async_read_until事後使用asio::async_read用於讀取的字節的確切的量對於HEADER以檢測消息的開始(前同步碼),並PAYLOAD + CRC。由於消息末尾沒有靜態模式,因此我無法使用async_read_until來閱讀完整消息。

收到PREAMBLE後,async_read_until的處理程序被調用,緩衝區包含PREAMBLE字節,並可能包含來自HEADER和PAYLOAD + CRC的附加字節。 爲async_read_until的ASIO文件說:

成功async_read_until操作後,流緩衝可以 含有超出分隔符附加數據。應用程序 通常將該數據保留在streambuf中以供隨後的async_read_until操作檢查。

我解釋這是因爲你應該只消耗請求的字節,並將所有剩餘的字節留在緩衝區中作進一步讀取。 但是,由於數據已經存在於緩衝區中並且設備上沒有任何內容,因此所有連續的讀取塊都將被阻止。

該讀取是作爲小型狀態機processState實現的,其中根據要讀取的消息的哪一部分來註冊不同的處理程序。所有讀數都使用相同的bufferasio::streambuf)完成。在無限循環中調用processState

void processState() { 
    // register handler for incomming messages 
    std::cout << "state: " << parser_state << std::endl; 
    switch (parser_state) { 
    case READ_PREAMBLE: 
     asio::async_read_until(port, buffer, "$M", 
      std::bind(&Client::onPreamble, this, std::placeholders::_1, std::placeholders::_2)); 
     break; 
    case READ_HEADER: 
     asio::async_read(port, buffer, asio::transfer_exactly(3), 
      std::bind(&Client::onHeader, this, std::placeholders::_1, std::placeholders::_2)); 
     break; 
    case READ_PAYLOAD_CRC: 
     asio::async_read(port, buffer, asio::transfer_exactly(request_received->length+1), 
      std::bind(&Client::onDataCRC, this, std::placeholders::_1, std::placeholders::_2)); 
     break; 
    case PROCESS_PAYLOAD: 
     onProcessMessage(); 
     break; 
    case END: 
     parser_state = READ_PREAMBLE; 
     break; 
    } 
    // wait for incoming data 
    io.run(); 
    io.reset(); 
} 

序言處理器onPreamble接收到前導時,被稱爲:

void onPreamble(const asio::error_code& error, const std::size_t bytes_transferred) { 
    std::cout << "onPreamble START" << std::endl; 
    if(error) { return; } 

    std::cout << "buffer: " << buffer.in_avail() << "/" << buffer.size() << std::endl; 

    // ignore and remove header bytes 
    buffer.consume(bytes_transferred); 

    std::cout << "buffer: " << buffer.in_avail() << "/" << buffer.size() << std::endl; 

    buffer.commit(buffer.size()); 

    std::cout << "onPreamble END" << std::endl; 
    parser_state = READ_HEADER; 
} 

此處理後,沒有其他的處理程序被調用,因爲數據是在緩衝區中,沒有數據留在設備上。

使用asio::streambuf的正確方法是什麼,使得調用連續的async_read的處理程序並且我可以按照狀態機的順序處理字節?我不想處理onPreamble中的剩餘字節,因爲不能保證這些字節將包含完整的消息。

回答

0

您不需要在onPreamble()處理程序中調用buffer.commit()。調用buffer.consume()將按照您的預期去除標題字節,並在asio::streambuf中留下剩餘的字節(如果有的話)以供下一次讀取。當您填寫要發送給遠程方的數據時,會使用流緩衝的prepare()commit()調用。

我剛剛完成了一個blog post and codecast關於使用asio :: streambuf執行簡單的HTTP GET與幾個Web服務器。它可能會讓您更好地瞭解如何使用async_read_until()async_read()

相關問題