簡體   English   中英

我不知道為什么使用 boost asio 的代碼會導致錯誤

[英]I don't know why code using boost asio is causing an error

我創建了主 cpp 文件和三個類來創建異步服務器。 分別是ServerServiceAcceptor 然而,它們在構建過程中導致了錯誤,即使在 Visual Studio 2019 環境中沒有錯誤。 我試圖修復錯誤,但大多數錯誤發生在其他文件中,所以我自己也想不出來。

main

#include "Server.h"
#include <iostream>
#include <boost/asio.hpp>
#include <thread>
#include <atomic>
#include <memory>
#define DEFAULT_THREAD_SIZE 2;
using namespace boost;
int main()
{
 unsigned short port_num;
    std::cin >> port_num;
    try {
        Server srv;
        unsigned int threads = std::thread::hardware_concurrency() * 2;
        if (threads == 0) {
            threads = DEFAULT_THREAD_SIZE;
        }
        std::cout << "\nPort - " << port_num << "\nServer start\n";
        srv.Start(port_num, threads);
        while (1) {
            std::cin >> srv;
        }
    }
    catch (system::system_error& e) {
        std::cout << "\nError code: " << e.code() << "\nError Message\n" << e.what();
    }
    return 0;
}

這包括 Server.h,它定義了服務器 class。

#include "Acceptor.h"
#include <boost/asio.hpp>
#include <thread>
#include <atomic>
#include <memory>
using namespace boost;
class Server
{
public:
    Server();
    void Start(unsigned short port_num, unsigned int threads);
    void Stop();
    int Command(std::string& str);
private:
    asio::io_service mios;
    std::unique_ptr<asio::io_service::work> mWork;
    std::unique_ptr<Acceptor> mAcceptor;
    std::vector <std::unique_ptr<std::thread>> mThreads;
};

std::istream& operator>>(std::istream& is, Server& srv);

這是實現,Server.cpp。

#include "Server.h"
Server::Server() {
    mWork.reset(new asio::io_service::work(mios));
}
void Server::Start(unsigned short port_num, unsigned int threads) {
    assert(thread > 0);
    mAcceptor.reset(new Acceptor(mios, port_num));
    mAcceptor->Start();
    for (int i = 0; i < threads; i++) {
        std::unique_ptr<std::thread> th(new std::thread([this]() {mios.run(); }));
        mThreads.push_back(std::move(th));
    }
}
void Server::Stop() {
    mAcceptor->Stop();
    mios.stop();

    for (auto& th : mThreads) {
        th->join();
    }
}
int Server::Command(std::string& str) {
    return 0;
}
std::istream& operator>>(std::istream& is, Server& srv) {
    std::string str;
    is >> str;
    srv.Command(str);
    return is;
}

這是接受器 class。

#include "Service.h"
#include <boost/asio.hpp>
#include <thread>
#include <atomic>
#include <memory>
using namespace boost;
class Acceptor
{
public:
    Acceptor(asio::io_service& ios, unsigned short port_num);
    void Start();
    void Stop();
private:
    std::shared_ptr<asio::io_service> mios;
    std::shared_ptr<asio::ip::tcp::acceptor> mAcceptor;
    std::atomic<bool> mIsStopped;
    void InitAccept();
    void OnAccept(const system::error_code ec, std::shared_ptr<asio::ip::tcp::socket> sock);
};
#include "Acceptor.h"
Acceptor::Acceptor(asio::io_service& ios, unsigned short port_num) {
    mios = std::make_shared<asio::io_service>(ios);
    mAcceptor = std::make_shared<asio::ip::tcp::acceptor>(mios, asio::ip::tcp::endpoint(asio::ip::address_v4::any(), port_num));
    mIsStopped = false;
}
void Acceptor::Start() {
    mAcceptor->listen();
    InitAccept();
}
void Acceptor::Stop() {
    mIsStopped.store(true);
}

void Acceptor::InitAccept() {
    std::shared_ptr<asio::ip::tcp::socket> sock(new asio::ip::tcp::socket(mios));
    mAcceptor->async_accept(*sock, [this, sock](const system::error_code& error) {OnAccept(error, sock);});
}
void Acceptor::OnAccept(const system::error_code ec, std::shared_ptr<asio::ip::tcp::socket> sock) {
    if (ec.value() == 0 || ER) {
        (new Service(sock))->StartHandling();
    }
    else{
        std::cout << "Error code:" << ec.value() << "error " << "Error message: " << ec.message() << "\n";
    }
    if (!mIsStopped.load()) {
        InitAccept();
    }
    else {
        mAcceptor->close();
    }
}

服務 class

#define ER true
#include <iostream>
#include <boost/asio.hpp>
#include <thread>
#include <atomic>
#include <memory>
using namespace boost;
class Service
{
public:
    Service(std::shared_ptr<asio::ip::tcp::socket> sock);
    void StartHandling();
private:
    void OnRequestReceived(const boost::system::error_code& ec, std::size_t bytes_transferred);
    std::string mReponse;
    std::shared_ptr<asio::ip::tcp::socket> mSock;
    asio::streambuf mRequest;
    void OnReponseSent(const system::error_code& ec, std::size_t bytes_transferred);

    void OnFinish();
    std::string ProcessRequest(asio::streambuf& request);
};
#include "Service.h"
Service::Service(std::shared_ptr<asio::ip::tcp::socket> sock){
    mSock = sock;
}
void Service::StartHandling() {
    asio::async_read_until(mSock, mRequest, '\n', [this](const system::error_code ec, std::size_t bytes_transferred) {OnRequestReceived(ec, bytes_transferred); });
}

void Service::OnRequestReceived(const system::error_code& ec, std::size_t bytes_transferred) {
    if (ec.value() != 0 || ER) {
        std::cout << "Error code:" << ec.value() << "Error message: " << ec.message() << "\n";
        OnFinish();
        return;
    }
    mReponse = ProcessRequest(mRequest);
    asio::async_write(mSock, asio::buffer(mReponse), [this](const system::error_code& ec, std::size_t bytes_transferred) {OnReponseSent(ec, bytes_transferred); });
}

void Service::OnReponseSent(const system::error_code& ec, std::size_t bytes_transferred) {
    if (ec.value() != 0 || ER) {
        std::cout << "Error code:" << ec.value() << "Error message: " << ec.message() << "\n";
    }
    OnFinish();
}

void Service::OnFinish() {
    delete this;
}
std::string Service::ProcessRequest(asio::streambuf& request) {
    std::string reponse;
    std::istream input(&request);
    std::getline(input, reponse);
    assert(reponse.back() == '\n');
    return reponse;
}

我不知道該怎么做。 我想自己做,但我什至無法調試,因為我不知道問題出在哪里,而且它沒有構建。

它根本無法編譯。 我真的很想知道人們如何在注意到這些東西無法編譯之前想出 /so much/ 代碼。

規則#1:嬰兒步驟(這同樣適用於專業人士,只是他們將其內化)。

  • 你正在做這樣的事情:

     mios = std::make_shared<asio::io_service>(ios);

    這要求io_service是可復制的(它不是)。 您可能會將mios作為參考:

     asio::io_service& mios;
  • 周圍似乎有很多“迷信”的 shared_ptr 使用。

  • 事實是

    assert(thread > 0);

    拼寫錯誤的threads表明您可能一直在構建僅發布版本。

  • 閱讀編譯器消息:

     void Service::StartHandling() { asio::async_read_until(mSock, mRequest, '\n', [this](const system::error_code ec, std::size_t bytes_transferred) {OnRequestReceived(ec, bytes_transferred); }); }

    這會觸發錯誤:

     /home/sehe/custom/boost_1_73_0/boost/asio/impl/read_until.hpp|959 col 53| error: no type named 'executor_type' in 'class std::shared_ptr<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >'

    顯然你的意思是*mSock 以后一樣:

     asio::async_write(*mSock, asio::buffer(mReponse), [this](const system::error_code& ec, std::size_t bytes_transferred) {OnReponseSent(ec, bytes_transferred); });

    指針不是它指向的 object - 甚至不是智能指針。 智能指針的要點 [原文如此] 不是讓 C++ 等於(比如說) Java - 如果你願意,你應該使用 Java。

有了這些,它編譯: Live ON Wandbox

更多評論

  • 頂級 const 對值 arguments 沒有影響

  • 不要使用newdelete

     mWork.reset(new asio::io_service::work(mios));

    改用make_unique

     mWork = std::make_unique<asio::io_service::work>(mios); //... mAcceptor = std::make_unique<Acceptor>(mios, port_num);
  • 使用 header 防護(或#pragma once

  • 不要使用命名空間 using 指令; 改用 using 聲明

  • 特別是不要在 header 文件中使用命名空間 using-directives(您使您的用戶無法防止/修復名稱沖突,這可能會導致編譯錯誤行為的靜默變化)

  • 使用構造函數初始化列表(和移動語義):

     Service::Service(std::shared_ptr<asio::ip::tcp::socket> sock){ mSock = sock; }

    變成

     Service::Service(std::shared_ptr<asio::ip::tcp::socket> sock): mSock(std::move(sock)) { }
  • 這里:

     (new Service(std::move(sock)))->StartHandling();

    不要使用 new,不要迷信使用共享指針,諷刺的是,在Service的情況下考慮使用enable_shared_from_this所以使用shared_ptr而不是delete this; 反模式。

  • 初始化您的原始 class 成員1

     std::atomic<bool> mIsStopped{};

    沒有,它會有不確定的價值,這通常會在使用時導致UB

  • 不要忽略錯誤:

     if (ec.value() == 0 || ER) { (new Service(std::move(sock)))->StartHandling(); }

    相反,報告/記錄。 此外,可移植地檢測錯誤:

     if (!ec) {

    或者

     if (.ec.failed()) {
  • 通常,處理錯誤(例如cin >> port_num ),

  • 由 const& 捕獲

中間結果(仍在編譯): Live on Wandbox

獎金

  • 簡化,使用asio::thread_pool ,統一初始化

  • 使用bytes_transferred read_until保證它在分隔符上停止,因為這不是 TCP 的工作方式。 尾隨數據可以存在於緩沖區中。 這意味着在 DEBUG 構建中,此斷言有時會失敗:

     assert(request.back() == '\n');

    實際上,代碼讀取response.back()肯定會失敗,因為getline不包含它¯\ (ツ)

    您可以在std::string上使用boost::iostreams::restrictasio::dynamic_buffer()並將string_view傳遞給處理程序( ProcessRequest ):

     mReponse = ProcessRequest(std::string_view(mRequest).substr(0, bytes_transferred));

    然后

     #include <boost/iostreams/device/array.hpp> #include <boost/iostreams/stream_buffer.hpp> std::string Service::ProcessRequest(std::string_view request) { assert(request.back() == '\n'); boost::iostreams::stream_buffer<boost::iostreams::array_source> buf( request.data(), request.size()); std::istream input(&buf); std::string reponse; std::getline(input, reponse); return reponse; }
  • 擺脫所有冗余的共享指針。 如果Acceptor已經由共享指針動態分配管理,則實際上沒有必要通過shared_ptr 使其擁有其tcp::acceptor實例。 一般來說,所有成員都可能只是代碼中的值。 只要周圍的 object 留在附近(就像您對服務所做的那樣),成員也可以保證保持活力。

  • mIsStopped 可以通過簡單地cancel()來消除接受器。 要獲得線程安全,只需發布到相關的執行者。

  • 如果您希望服務器在執行停止命令時真正退出,則需要使while(true)循環具有停止條件,例如

     int Server::Command(std::string const& cmd) { std::cout << "Command: " << std::quoted(cmd) << "\n"; if (cmd == "quit") { Stop(); return 1; } std::cerr << "Unknown command (\"quit\" to exit)" << std::endl; return 0; } std::istream& operator>>(std::istream& is, Server& srv) { std::string str; is >> str; if (srv.Command(str)) { is.setstate(std::ios::badbit); } return is; }

    main是:

     while (std::cin >> srv) { }

完整演示:

活在魔杖盒上

  • 文件Acceptor.h

     #ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_ACCEPTOR_H #define _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_ACCEPTOR_H #include "Service.h" class Acceptor { public: template <typename Executor> Acceptor(Executor ex, unsigned short port_num): mAcceptor(make_strand(ex), {{}, port_num}) {} void Start(); void Stop(); private: tcp::acceptor mAcceptor; void InitAccept(); void OnAccept(error_code ec, tcp::socket&& sock); }; #endif
  • 文件Common.h

     #pragma once #include <boost/asio.hpp> #include <memory> #include <thread> #include <atomic> namespace asio = boost::asio; using boost::system::error_code; using asio::ip::tcp;
  • 文件Server.h

     #ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVER_H #define _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVER_H #include "Acceptor.h" class Server { public: explicit Server(unsigned short port_num); void Start(); void Stop(); int Command(std::string const& str); private: asio::thread_pool mio; Acceptor mAcceptor; }; std::istream& operator>>(std::istream& is, Server& srv); #endif
  • 文件Service.h

     #ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVICE_H #define _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVICE_H #include "Common.h" #include <iostream> class Service: public std::enable_shared_from_this<Service> { public: explicit Service(tcp::socket&& sock); void StartHandling(); private: void OnRequestReceived(error_code ec, std::size_t bytes_transferred); std::string mRequest, mReponse; tcp::socket mSock; void OnReponseSent(error_code ec, size_t bytes_transferred); std::string ProcessRequest(std::string_view request); }; #endif
  • 文件Acceptor.cpp

     #include "Acceptor.h" #include <utility> void Acceptor::Start() { mAcceptor.listen(); InitAccept(); } void Acceptor::Stop() { // be thread safe post(mAcceptor.get_executor(), [this] { mAcceptor.cancel(); }); } void Acceptor::InitAccept() { mAcceptor.async_accept( make_strand(mAcceptor.get_executor()), [this](error_code error, tcp::socket&& sock) { OnAccept(error, std::move(sock)); }); } void Acceptor::OnAccept(error_code ec, tcp::socket&& sock) { if (.ec:failed()) { std::make_shared<Service>(std:;move(sock))->StartHandling(); InitAccept(): } else { std::cout << "OnAccept. " << ec;message() << "\n"; } }
  • 文件main.cpp

     #include "Server.h" #include <iostream> int main() { if (uint16_t port_num; std::cin >> port_num) { try { Server srv(port_num); std::cout << "Port - " << port_num << "\nServer start\n"; srv.Start(); while (std::cin >> srv) { } } catch (boost::system::system_error const& e) { std::cout << "Error " << e.code().message() << "\n"; } } else { std::cerr << "Invalid input (port number required)\n"; } }
  • 文件Server.cpp

     #include "Server.h" #include <iomanip> Server::Server(unsigned short port_num): mAcceptor(make_strand(mio), port_num) {} void Server::Start() { mAcceptor.Start(); } void Server::Stop() { mAcceptor.Stop(); } int Server::Command(std::string const& cmd) { std::cout << "Command: " << std::quoted(cmd) << "\n"; if (cmd == "quit") { Stop(); return 1; } std::cerr << "Unknown command (\"quit\" to exit)" << std::endl; return 0; } std::istream& operator>>(std::istream& is, Server& srv) { std::string str; is >> str; if (srv.Command(str)) { is.setstate(std::ios::badbit); } return is; }
  • 文件Service.cpp

     #include "Service.h" #include <utility> #include <iomanip> Service::Service(tcp::socket&& sock): mSock(std::move(sock)) {} void Service::StartHandling() { asio::async_read_until( mSock, asio::dynamic_buffer(mRequest), '\n', [this, self = shared_from_this()](error_code ec, std::size_t bytes_transferred) { OnRequestReceived(ec, bytes_transferred); }); } void Service::OnRequestReceived(error_code ec, std::size_t bytes_transferred) { if (ec) { std::cout << "OnRequestReceived: " << ec.message() << "\n"; return; } std::string_view view = mRequest; mReponse = ProcessRequest(view.substr(0, bytes_transferred)); asio::async_write( mSock, asio::buffer(mReponse), [this, self = shared_from_this()](error_code ec, std::size_t bytes_transferred) { OnReponseSent(ec, bytes_transferred); }); } void Service::OnReponseSent(error_code ec, std::size_t /*bytes_transferred*/) { if (ec) { std::cout << "OnReponseSent: " << ec.message() << "\n"; } } #include <boost/iostreams/device/array.hpp> #include <boost/iostreams/stream_buffer.hpp> std::string Service::ProcessRequest(std::string_view request) { //std::cerr << "TRACE: " << std::quoted(request) << "\n"; assert(request.back() == '\n'); boost::iostreams::stream_buffer<boost::iostreams::array_source> buf( request.data(), request.size()); std::istream input(&buf); std::string reponse; std::getline(input, reponse); return reponse + '\n'; }

例如,當使用2323和更高版本的quit命令運行時:

# (echo 2323; sleep 30; echo quit) | ./sotest
Port - 2323
Server start
Command: "quit"
OnAccept: Operation canceled

它確實正確接受多個連接:

# for a in {1..10}; do printf "Message with random data $RANDOM\n" | nc localhost 2323; done
Message with random data 8002
Message with random data 28046
Message with random data 17943
Message with random data 17845
Message with random data 10832
Message with random data 20049
Message with random data 27593
Message with random data 18979
Message with random data 2773
Message with random data 31159

暫無
暫無

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

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