2015-09-21 50 views
1

我一直在線上學習Asio的異步網絡,所以如果我犯了一個非常明顯的錯誤,那就是你的解釋。異步讀取完成,但緩衝區不包含預期的結果

儘管如此,我寫了一個程序,它同時設置客戶端和服務器,並嘗試在兩者之間進行通信。簡單地連接併發送請求以發送/接收數據似乎工作正常,但數據本身並未發送。

#define ASIO_STANDALONE 
#include<asio.hpp> 
#include<thread> 
#include<iostream> 
#include<vector> 
#include<array> 
#include<mutex> 
#include<memory> 
#include<functional> 

#define IPADDRESS "127.0.0.1" 
#define PORT  "6118" 

enum side_type { 
    t_server, t_client 
}; 

std::mutex m_lock; 
std::array<char, 32> clientBuffer; 
std::array<char, 32> serverBuffer; 
bool stop(false); 

void read_function(const asio::error_code&, size_t, std::shared_ptr<asio::ip::tcp::socket>, std::array<char, 32> &, side_type &); 
void write_function(const asio::error_code&, size_t, std::shared_ptr<asio::ip::tcp::socket>, std::array<char, 32> &, side_type &); 

void read_function(const asio::error_code& ec, size_t bytes_read, std::shared_ptr<asio::ip::tcp::socket> socket, std::array<char, 32> & buffer, side_type & type) { 
    if (ec) return; 
    using namespace std; 
    using namespace std::placeholders; 
    char value = buffer[0]; 
    { 
     lock_guard<mutex> guard(m_lock); 
     string type_str = type == t_server ? "Server" : "Client"; 
     cout << "Value of " << int(value) << " read by " << type_str << "." << endl; 
    } 
    if (value >= 100) stop = true; 
    else { 
     if(type == t_server) 
      buffer[0] = value + 1; 
     socket->async_write_some(asio::buffer(&buffer[0], buffer.max_size()), bind(write_function, _1, _2, socket, buffer, type)); 
    } 
} 

void write_function(const asio::error_code& ec, size_t bytes_written, std::shared_ptr<asio::ip::tcp::socket> socket, std::array<char, 32> & buffer, side_type & type) { 
    if (ec) return; 
    using namespace std; 
    using namespace std::placeholders; 
    socket->async_read_some(asio::buffer(&buffer[0], buffer.max_size()), bind(read_function, _1, _2, socket, buffer, type)); 
} 

void work_function(std::shared_ptr<asio::io_service> io_service) { 
    using namespace std; 
    asio::error_code ec; 
    while (!ec) { 
     try { 
      io_service->run(ec); 
      break; 
     } 
     catch (exception & e) { 
      lock_guard<mutex> guard(m_lock); 
      cout << "Exception thrown: \"" << e.what() << "\"." << endl; 
     } 
    } 
} 

void connect_function(const asio::error_code & ec, std::shared_ptr<asio::ip::tcp::socket> socket) { 
    using namespace std; 
    using namespace std::placeholders; 
    lock_guard<mutex> guard(m_lock); 
    if (ec) { 
     cout << "Error Connecting: " << ec << endl; 
    } 
    else { 
     cout << "Successful Connection!" << endl; 
     socket->async_read_some(asio::buffer(&clientBuffer[0], clientBuffer.max_size()), bind(read_function, _1, _2, socket, clientBuffer, t_client)); 
    } 
} 

void accept_function(const asio::error_code & ec, std::shared_ptr<asio::ip::tcp::socket> socket) { 
    using namespace std; 
    using namespace std::placeholders; 
    lock_guard<mutex> guard(m_lock); 
    if (ec) { 
     cout << "Error Accepting: " << ec << endl; 
    } 
    else { 
     cout << "Successful Acception!" << endl; 
     serverBuffer[0] = 0; 
     socket->async_write_some(asio::buffer(&serverBuffer[0], serverBuffer.max_size()), bind(write_function, _1, _2, socket, serverBuffer, t_server)); 
    } 
} 

int main(int argc, char** argv) { 
    using namespace std; 
    using namespace std::placeholders; 
    shared_ptr<asio::io_service> io_service(new asio::io_service()); 
    shared_ptr<asio::io_service::work> work(new asio::io_service::work(*io_service)); 

    vector<shared_ptr<thread>> threads; 
    int num_of_threads = thread::hardware_concurrency(); 
    for (auto i = 0; i < thread::hardware_concurrency(); i++) { 
     threads.push_back(shared_ptr<thread>(new thread(work_function, io_service))); 
    } 

    using namespace asio::ip; 
    tcp::resolver resolver(*io_service); 
    tcp::resolver::query query(IPADDRESS, PORT); 
    tcp::resolver::iterator iterator = resolver.resolve(query); 
    tcp::endpoint endpoint = *iterator; 

    cout << "Connecting to " << endpoint << endl; 

    shared_ptr<tcp::acceptor> acceptor(new tcp::acceptor(*io_service)); 
    shared_ptr<tcp::socket> acc_socket(new tcp::socket(*io_service)); 
    shared_ptr<tcp::socket> socket(new tcp::socket(*io_service)); 

    acceptor->open(endpoint.protocol()); 
    acceptor->set_option(tcp::acceptor::reuse_address(false)); 
    acceptor->bind(endpoint); 
    acceptor->listen(asio::socket_base::max_connections); 
    acceptor->async_accept(*acc_socket, bind(accept_function, _1, acc_socket)); 

    asio::error_code ec; 
    socket->async_connect(endpoint, bind(connect_function, _1, socket)); 

    //while (!stop); 

    cout << "Press Any Key to Continue..." << endl; 
    cin.get(); 

    socket->shutdown(tcp::socket::shutdown_both, ec); 
    socket->close(ec); 

    work.reset(); 

    while (!io_service->stopped()); 

    for (shared_ptr<thread> & t : threads) { 
     t->join(); 
    } 

    return 0; 
} 

作爲輸出,我已經得到了以下內容:

Connecting to 127.0.0.1:6118 
Press Any Key to Continue... 
Successful Connection! 
Successful Acception! 
Value of 0 read by Client. 
Value of 0 read by Server. 
Value of 0 read by Client. 
Value of 1 read by Server. 
Value of 0 read by Client. 
Value of 2 read by Server. 
Value of 0 read by Client. 
Value of 3 read by Server. 
...... 
Value of 0 read by Client. 
Value of 98 read by Server. 
Value of 0 read by Client. 
Value of 99 read by Server. 
Value of 0 read by Client. 
Value of 100 read by Server. 

不過,我很期待是:

Connecting to 127.0.0.1:6118 
Press Any Key to Continue... 
Successful Connection! 
Successful Acception! 
Value of 0 read by Client. 
Value of 0 read by Server. 
Value of 1 read by Client. 
Value of 1 read by Server. 
Value of 2 read by Client. 
Value of 2 read by Server. 
Value of 3 read by Client. 
Value of 3 read by Server. 
...... 
Value of 98 read by Client. 
Value of 98 read by Server. 
Value of 99 read by Client. 
Value of 99 read by Server. 
Value of 100 read by Client. 
Value of 100 read by Server. 

顯然發生了什麼是服務器緩衝區得到更新(當我手動增加值時),而Client緩衝區永遠不會被async_read_some函數更新。此外,由於客戶端緩衝區永遠不會更新,因此服務器只讀舊值(也未更新),因此在技術上也具有不正確的輸出。但是,我不知道什麼是錯的。我按照我認爲應該的方式傳遞所有緩衝區,並且所有函數似乎都被正確綁定,但數據未被傳遞。那麼我做錯了什麼?

+0

什麼版本提升您使用的是? – JVene

+0

或者,如果不使用boost,什麼版本的asio? – JVene

+0

我正在使用Asio的非Boost版本。版本1.10.6。下面是最新的非開發版代碼:http://think-async.com/Asio/Download – Xirema

回答

1

的問題是,緩衝的副本被綁定到完成處理程序,它是一個不同的緩衝器比其被提供給所述的異步操作:

socket->async_read_some(asio::buffer(buffer), std::bind(..., buffer, ...)); 
            // ^~~~~~ = reference  ^~~~~~ = copy 

在上述片段中,async_read_some()操作將在buffer上運行,完成處理程序將在操作作出任何修改之前提供buffer的副本。要解決此問題,請使用std::ref()將引用傳遞給std::bind()

socket->async_read_some(asio::buffer(buffer), std::bind(..., std::ref(buffer), ...)); 
            // ^~~~~~ = reference    ^~~~~~ = reference 

在這種情況下,傳遞引用也將修復一個潛在的情況,未定義的行爲可能已被調用。 async_write_some()async_read_some()操作要求調用者保留底層緩衝存儲器的所有權,調用者必須保證它在調用完成處理程序之前保持有效。當std::bind()被提供了緩衝區的副本時,緩衝區的生存期被綁定到從std::bind()返回的函子對象,該對象可能在調用完成處理程序之前結束。

void read_function(
    ..., 
    std::shared_ptr<asio::ip::tcp::socket> socket, 
    std::array<char, 32>& buffer, 
    ...) 
{ 
    ... 
    socket->async_write_some(asio::buffer(buffer), handler); 
} // buffer's lifetime ends shortly after returning from this function 

socket->async_read_some(
    asio::buffer(buffer), 
    std::bind(&read_function, ..., socket, buffer, ...)); 

下面是一個例子demonstrating的根本問題和行爲:

#include <array> 
#include <cassert> 
#include <functional> 

int get_data(std::array<char, 32>& data) 
{ 
    return data[0]; 
} 

int main() 
{ 
    std::array<char, 32> data; 
    data[0] = 0; 
    auto fn_copy = std::bind(&get_data, data); 
    auto fn_ref = std::bind(&get_data, std::ref(data)); 
    data[0] = 1; 
    assert(0 == fn_copy()); 
    assert(1 == fn_ref()); 
} 
+0

精彩的寫作,這解決了我的問題。謝謝! – Xirema

-1

ReadhandlerWriteHander

void read_function(const asio::error_code&, size_t, std::shared_ptr<asio::ip::tcp::socket>, std::array<char, 32> &, side_type &); 
void write_function(const asio::error_code&, size_t, std::shared_ptr<asio::ip::tcp::socket>, std::array<char, 32> &, side_type &); 

不符合ASIO Read handlerWrite handler要求。即只是:

void read_function(const asio::error_code&, size_t); 
void write_function(const asio::error_code&, size_t); 

您的應用程序需要「擁有」讀取和寫入緩衝區,並且不希望處理程序將它們的位置發回給您。如果您在適當的地方使用clientBufferserverBuffer,它應該可以正常工作。

+0

提供給操作的ReadHandler和WriteHandler確實符合處理程序的要求,因爲從' std :: bind()'符合'h(ec,s')的表達式要求,其中'ec'是一個左值'const error_code','s'是一個左值'const size_t'。 Asio執行一些概念檢查並且未能滿足類型要求通常會導致編譯錯誤。 –

+0

@TannerSansbury您的評論是不必要的,但不完全正確。這樣的恥辱,我曾經尊敬你... – kenba

+0

發表我的評論時,我的意思是沒有不良行爲或不尊重。我試圖強調,雖然'read_function'和'write_function'不符合相應的處理程序類型要求,但可以使用'std :: bind()'來滿足這些要求,就像在原始問題中所做的那樣。試圖提供關於潛在混淆點的信息給那些仍在學習圖書館的人是一個不好的選擇?另外,如果所提供的信息不是嚴格正確的,那麼請改進它,併爲他人的利益提供更正的信息。 –