简体   繁体   English

我不知道为什么使用 boost asio 的代码会导致错误

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

I created the main cpp file and three classes to create an asynchronous server.我创建了主 cpp 文件和三个类来创建异步服务器。 Server , Service , and Acceptor respectively.分别是ServerServiceAcceptor However, they caused errors in the build process, even though there were no errors in the visual studio 2019 environment.然而,它们在构建过程中导致了错误,即使在 Visual Studio 2019 环境中没有错误。 I tried to fix the error, but most of the errors occurred in other files, so I couldn't even think of it myself.我试图修复错误,但大多数错误发生在其他文件中,所以我自己也想不出来。

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

This includes Server.h, which defines Server class.这包括 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);

Here's the implementation, Server.cpp.这是实现,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;
}

This is Acceptor class.这是接受器 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();
    }
}

Service class服务 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;
}

I have no idea what to do.我不知道该怎么做。 I wanted to do it myself, but I couldn't even debug because I couldn't figure out where the problem was going and it wasn't built.我想自己做,但我什至无法调试,因为我不知道问题出在哪里,而且它没有构建。

It simply doesn't compile.它根本无法编译。 I genuinely wonder how people can come up with /so much/ code before noticing that stuff doesn't compile.我真的很想知道人们如何在注意到这些东西无法编译之前想出 /so much/ 代码。

Rule #1: Baby Steps (this goes for the professionals just as much, only they have it internalized).规则#1:婴儿步骤(这同样适用于专业人士,只是他们将其内化)。

  • You're doing stuff like:你正在做这样的事情:

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

    This requires io_service to be copyable (which it isn't).这要求io_service是可复制的(它不是)。 You would probably make mios a reference:您可能会将mios作为参考:

     asio::io_service& mios;
  • There seems to be a lot of "superstitious" use of shared_ptr all around.周围似乎有很多“迷信”的 shared_ptr 使用。

  • The fact that事实是

    assert(thread > 0);

    misspelled threads indicates that you may have been building Release-only builds.拼写错误的threads表明您可能一直在构建仅发布版本。

  • Read the compiler messages:阅读编译器消息:

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

    This triggers the error:这会触发错误:

     /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> >'

    Obviously you meant *mSock .显然你的意思是*mSock Same later:以后一样:

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

    A pointer is not the object it points to - not even smart pointers.指针不是它指向的 object - 甚至不是智能指针。 The point [sic] of smart pointers is not to make C++ equal to (say) Java - you should use Java if you wanted that.智能指针的要点 [原文如此] 不是让 C++ 等于(比如说) Java - 如果你愿意,你应该使用 Java。

With these it compiles: Live ON Wandbox有了这些,它编译: Live ON Wandbox

More Review更多评论

  • top-level const makes no difference in value arguments顶级 const 对值 arguments 没有影响

  • Don't use new or delete :不要使用newdelete

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

    use make_unique instead改用make_unique

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

  • Do not use namespace using-directives;不要使用命名空间 using 指令; use using-declarations instead改用 using 声明

  • Especially don't use namespace using-directives in header files (you make it impossible for your users to prevent/fix name collisions, which may lead to compile error or silent change of behaviour)特别是不要在 header 文件中使用命名空间 using-directives(您使您的用户无法防止/修复名称冲突,这可能会导致编译错误行为的静默变化)

  • Use constructor initializer lists (and move-semantics):使用构造函数初始化列表(和移动语义):

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

    Becomes变成

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

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

    Don't use new, don't superstitious-use shared pointer, and, ironically, in the case of Service consider using enable_shared_from_this so you do use shared_ptr instead of the delete this;不要使用 new,不要迷信使用共享指针,讽刺的是,在Service的情况下考虑使用enable_shared_from_this所以使用shared_ptr而不是delete this; anti-pattern.反模式。

  • Initialize your primitive class members1初始化您的原始 class 成员1

     std::atomic<bool> mIsStopped{};

    Without, it will have indeterminate value, which usually leads to UB when used没有,它会有不确定的价值,这通常会在使用时导致UB

  • Don't ignore errors:不要忽略错误:

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

    Instead, report / log.相反,报告/记录。 Also, detect errors portably:此外,可移植地检测错误:

     if (!ec) {

    Or或者

     if (.ec.failed()) {
  • generally, handle errors ( cin >> port_num eg),通常,处理错误(例如cin >> port_num ),

  • catch by const&由 const& 捕获

Intermediate result (still compiles): Live on Wandbox中间结果(仍在编译): Live on Wandbox

BONUS奖金

  • Simplify, use asio::thread_pool , uniform initalization简化,使用asio::thread_pool ,统一初始化

  • USE bytes_transferred !使用bytes_transferred read_until does not guarantee it stops on the delimiter, because that's not how TCP works. read_until保证它在分隔符上停止,因为这不是 TCP 的工作方式。 Trailing data can be present in the buffer.尾随数据可以存在于缓冲区中。 This means that in DEBUG builds this assert would sometimes fail:这意味着在 DEBUG 构建中,此断言有时会失败:

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

    Actually the code read response.back() which is guaranteed to fail because getline doesn't include it ¯\ (ツ)实际上,代码读取response.back()肯定会失败,因为getline不包含它¯\ (ツ)

    You might use boost::iostreams::restrict or instead asio::dynamic_buffer() on a std::string and pass a string_view into the handler ( ProcessRequest ):您可以在std::string上使用boost::iostreams::restrictasio::dynamic_buffer()并将string_view传递给处理程序( ProcessRequest ):

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

    And later然后

     #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; }
  • Get rid of all the redundant shared pointers.摆脱所有冗余的共享指针。 If Acceptor is already dynamically allocated managed by a shared-pointer, there is really no need to also make it own its tcp::acceptor instance by shared_ptr.如果Acceptor已经由共享指针动态分配管理,则实际上没有必要通过shared_ptr 使其拥有其tcp::acceptor实例。 In general all the members could just be by value in your code.一般来说,所有成员都可能只是代码中的值。 As long as the surrounding object stays around (as you do with Service) the members are guaranteed to stay alive as well.只要周围的 object 留在附近(就像您对服务所做的那样),成员也可以保证保持活力。

  • mIsStopped can be eliminated by simply cancel() -ing the acceptor instead. mIsStopped 可以通过简单地cancel()来消除接受器。 To get thread-safety, simply post to the relevant executor.要获得线程安全,只需发布到相关的执行者。

  • If you wanted the server to actually exit when the stop command is executed, you need to make the while(true) loop have a stop condition, eg如果您希望服务器在执行停止命令时真正退出,则需要使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; }

    And in main : main是:

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

FULL DEMO:完整演示:

Live On Wandbox活在魔杖盒上

  • File Acceptor.h文件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
  • File Common.h文件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;
  • File Server.h文件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
  • File Service.h文件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
  • File Acceptor.cpp文件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"; } }
  • File main.cpp文件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"; } }
  • File Server.cpp文件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; }
  • File Service.cpp文件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'; }

Eg when running with 2323 and later quit command:例如,当使用2323和更高版本的quit命令运行时:

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

It does correctly accept multiple connections:它确实正确接受多个连接:

# 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