簡體   English   中英

異步讀取完成,但是緩沖區不包含預期結果

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

我一直在網上關注大量有關在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.

顯然,正在發生的事情是服務器緩沖區正在更新(當我手動增加該值時),而客戶端緩沖區卻從未通過async_read_some函數進行更新。 另外,由於客戶端緩沖區永遠不會更新,因此服務器僅讀取舊值(也不更新),因此從技術上講,它的輸出也不正確。 但是,我不知道怎么了。 我以我認為應該的方式傳遞所有緩沖區,所有功能似乎都正確綁定,但未傳遞數據。 那我做錯了什么?

問題在於緩沖區的副本綁定到完成處理程序,該處理程序與為異步操作提供的緩沖區不同:

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, ...));

這是一個演示基本問題和行為的示例:

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

您的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 讀取處理程序寫入處理程序的要求。 即:

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

您的應用程序需要“擁有”讀寫緩沖區,並且不要期望處理程序將它們的位置發送回給您。 如果在適當的地方使用clientBufferserverBuffer ,它將正常工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM