2015-09-21 28 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()'来满足这些要求,就像在原始问题中所做的那样。试图提供关于潜在混淆点的信息给那些仍在学习图书馆的人是一个不好的选择?另外,如果所提供的信息不是严格正确的,那么请改进它,并为他人的利益提供更正的信息。 –