简体   繁体   English

异步读取完成,但是缓冲区不包含预期结果

[英]Async read completes, but buffer does not contain expected results

I've been following numerous tutorials online on learning Asynchronous Networking in Asio, so if I've made a really obvious mistake, there's your explanation. 我一直在网上关注大量有关在Asio中学习异步网络的教程,因此,如果我犯了一个非常明显的错误,那么您的解释就可以了。

Nonetheless, I've written a program that sets up both a client and server simultaneously and tries to communicate between the two. 尽管如此,我编写了一个程序,该程序同时设置了客户端和服务器,并尝试在两者之间进行通信。 Simply connecting and making requests to send/receive data seem to be working fine, but the data itself isn't being sent. 简单地连接并发出发送/接收数据的请求似乎可以正常工作,但是数据本身并未被发送。

#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;
}

As output, I've been getting the following: 作为输出,我得到了以下内容:

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.

However, what I'm expecting is: 但是,我期望的是:

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.

Clearly what's happening is that the Server buffer is getting updated (when I manually increment the value), while the Client Buffer never gets updated by the async_read_some function. 显然,正在发生的事情是服务器缓冲区正在更新(当我手动增加该值时),而客户端缓冲区却从未通过async_read_some函数进行更新。 Additionally, because the client buffer never gets updated, the server is just reading in old values (also without getting updated) and thus technically has incorrect output as well. 另外,由于客户端缓冲区永远不会更新,因此服务器仅读取旧值(也不更新),因此从技术上讲,它的输出也不正确。 However, I don't know what's wrong. 但是,我不知道怎么了。 I'm passing in all my buffers the way I think I'm supposed to, and all the functions seem to be bound correctly, but the data isn't being passed. 我以我认为应该的方式传递所有缓冲区,所有功能似乎都正确绑定,但未传递数据。 So what did I do wrong? 那我做错了什么?

The problem is that a copy of the buffer is being bound to the completion handler, which is a different buffer than that which is provided to the asynchronous operations: 问题在于缓冲区的副本绑定到完成处理程序,该处理程序与为异步操作提供的缓冲区不同:

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

In the above snippet, the async_read_some() operation will operate on buffer , and the completion handler will be provided a copy of buffer before the operation has made any modifications. 在上述代码段中, async_read_some()操作将对buffer进行操作,并且在完成处理程序进行任何修改之前,将为完成处理程序提供一个buffer的副本。 To resolve this, use std::ref() to pass a reference to std::bind() . 要解决此问题,请使用std::ref()将引用传递给std::bind()

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

In this case, passing a reference will also fix a potential case where undefined behavior could have been invoked. 在这种情况下,传递引用也将解决可能会调用未定义行为的潜在情况。 The async_write_some() and async_read_some() operations require that ownership of the underlying buffer memory is retained by the caller, who must guarantee that it remains valid until the completion handler is called. async_write_some()async_read_some()操作要求调用者保留对底层缓冲存储器的所有权,调用者必须保证其保持有效,直到调用完成处理程序为止。 When std::bind() was being provided a copy of the buffer, the buffer's lifetime was bound to the functor object returned from std::bind() , which may have ended before the completion handler was invoked. 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, ...));

Here is an example demonstrating the fundamental problem and behavior: 这是一个演示基本问题和行为的示例:

#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());
}

Your Readhandler and WriteHander : 您的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 &);

don't conform to the asio Read handler and Write handler requirements. 不符合asio 读取处理程序写入处理程序的要求。 Ie just: 即:

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

Your application needs to "own" the read and write buffers and not expect their locations to be sent back to you by the handlers. 您的应用程序需要“拥有”读写缓冲区,并且不要期望处理程序将它们的位置发送回给您。 If you use clientBuffer and serverBuffer where appropriate, it should work correctly. 如果在适当的地方使用clientBufferserverBuffer ,它将正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM