我创建了主 cpp 文件和三个类来创建异步服务器。
Server、Service、 和Acceptor分别。然而,它们在构建过程中导致了错误,即使在 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;
}
Run Code Online (Sandbox Code Playgroud)
这包括 Server.h,它定义了 Server 类。
#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);
Run Code Online (Sandbox Code Playgroud)
这是实现,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;
}
Run Code Online (Sandbox Code Playgroud)
这是接受器类。
#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);
};
Run Code Online (Sandbox Code Playgroud)
#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();
}
}
Run Code Online (Sandbox Code Playgroud)
服务等级
#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);
};
Run Code Online (Sandbox Code Playgroud)
#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;
}
Run Code Online (Sandbox Code Playgroud)
我不知道该怎么做。我想自己做,但我什至无法调试,因为我无法弄清楚问题出在哪里,并且没有构建。
它根本无法编译。我真的很想知道人们如何在注意到这些东西无法编译之前想出/这么多/代码。
\n规则#1:婴儿学步(这同样适用于专业人士,只是他们已经内化了)。
\n你正在做这样的事情:
\nmios = std::make_shared<asio::io_service>(ios);\nRun Code Online (Sandbox Code Playgroud)\n这需要io_service可复制(但事实并非如此)。您可能会做mios一个参考:
asio::io_service& mios;\nRun Code Online (Sandbox Code Playgroud)\n周围似乎有很多对shared_ptr 的“迷信”使用。
\n事实是
\nassert(thread > 0);\nRun Code Online (Sandbox Code Playgroud)\n拼写错误threads表示您可能正在构建仅限发布的版本。
阅读编译器消息:
\nvoid Service::StartHandling() {\n asio::async_read_until(mSock, mRequest, \'\\n\', [this](const system::error_code ec, std::size_t bytes_transferred) {OnRequestReceived(ec, bytes_transferred); });\n}\nRun Code Online (Sandbox Code Playgroud)\n这会触发错误:
\n/home/sehe/custom/boost_1_73_0/boost/asio/impl/read_until.hpp|959 col 53| error: no type named \xe2\x80\x98executor_type\xe2\x80\x99 in \xe2\x80\x98class std::shared_ptr<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >\xe2\x80\x99\nRun Code Online (Sandbox Code Playgroud)\n显然你的意思是*mSock。稍后同样:
asio::async_write(*mSock, asio::buffer(mReponse), [this](const system::error_code& ec, std::size_t bytes_transferred) {OnReponseSent(ec, bytes_transferred); });\nRun Code Online (Sandbox Code Playgroud)\n\n\n指针不是它指向的对象——甚至智能指针也不是。智能指针的要点 [原文如此] 并不是让 C++ 等于(比如)Java - 如果您想要的话,您应该使用 Java。
\n
有了这些,它就可以编译:Live ON Wandbox
\n顶级 const 对值参数没有影响
\n不要使用new或delete:
mWork.reset(new asio::io_service::work(mios));\nRun Code Online (Sandbox Code Playgroud)\n改用make_unique
mWork = std::make_unique<asio::io_service::work>(mios);\n // ...\n mAcceptor = std::make_unique<Acceptor>(mios, port_num);\nRun Code Online (Sandbox Code Playgroud)\n使用标题保护(或#pragma once)
不要使用命名空间 using-directives;使用 using 声明代替
\n特别是不要在头文件中使用命名空间使用指令(您的用户将无法防止/修复名称冲突,这可能会导致编译错误或行为的静默更改)
\n使用构造函数初始值设定项列表(和移动语义):
\n Service::Service(std::shared_ptr<asio::ip::tcp::socket> sock){\n mSock = sock;\n }\nRun Code Online (Sandbox Code Playgroud)\n成为
\n Service::Service(std::shared_ptr<asio::ip::tcp::socket> sock) \n : mSock(std::move(sock))\n { }\nRun Code Online (Sandbox Code Playgroud)\n这里:
\n (new Service(std::move(sock)))->StartHandling();\nRun Code Online (Sandbox Code Playgroud)\n不要使用 new,不要迷信使用共享指针,并且讽刺的是,在考虑Service使用的情况下enable_shared_from_this,你应该使用shared_ptr而不是delete this;反模式。
初始化您的原始类成员1
\n std::atomic<bool> mIsStopped{};\nRun Code Online (Sandbox Code Playgroud)\n如果没有,它将具有不确定的值,这通常会导致使用时出现UB
\n不要忽略错误:
\n if (ec.value() == 0 || ER) {\n (new Service(std::move(sock)))->StartHandling();\n }\nRun Code Online (Sandbox Code Playgroud)\n相反,报告/日志。另外,可移植地检测错误:
\n if (!ec) {\nRun Code Online (Sandbox Code Playgroud)\n或者
\n if (!ec.failed()) {\nRun Code Online (Sandbox Code Playgroud)\n一般来说,处理错误(cin >> port_num例如),
通过 const& 捕获
\n\n\n中间结果(仍可编译):Live on Wandbox
\n
奖金
\n简化、使用asio::thread_pool、统一初始化
使用bytes_transferred!read_until不它在分隔符处停止,因为那不是 TCP 的工作方式。缓冲区中可以存在尾随数据。这意味着在 DEBUG 构建中,此断言有时会失败:
assert(request.back() == \'\\n\');\nRun Code Online (Sandbox Code Playgroud)\n\n\n实际上,读取的代码
\nresponse.back()肯定会失败,因为getline不包含它 \xc2\xaf\\ (\xe3\x83\x84) /\xc2\xaf
您可以在 a 上使用boost::iostreams::restrictor 并将a 传递给\n处理程序 ( ):asio::dynamic_buffer()std::stringstring_viewProcessRequest
mReponse = ProcessRequest(std::string_view(mRequest).substr(0, bytes_transferred));\nRun Code Online (Sandbox Code Playgroud)\n然后
\n #include <boost/iostreams/device/array.hpp>\n #include <boost/iostreams/stream_buffer.hpp>\n\n std::string Service::ProcessRequest(std::string_view request) {\n assert(request.back() == \'\\n\');\n boost::iostreams::stream_buffer<boost::iostreams::array_source> buf(\n request.data(), request.size());\n\n std::istream input(&buf);\n std::string reponse;\n std::getline(input, reponse);\n return reponse;\n }\nRun Code Online (Sandbox Code Playgroud)\n摆脱所有多余的共享指针。如果Acceptor已经\n由共享指针动态分配管理,则实际上没有必要\n也通过shared_ptr使其拥有自己的tcp::acceptor实例。一般来说,\n所有成员都可以在代码中按值显示。只要\n周围的对象保持在周围(就像您对服务所做的那样),\n成员就保证保持活动状态。
cancel()mIsStopped 可以通过简单地对接受器进行 -ing 来消除。要获得线程安全,只需发布到相关的执行器即可。
如果你想让服务器在执行停止命令时真正退出,你需要让循环while(true)有一个停止条件,例如
int Server::Command(std::string const& cmd) {\n std::cout << "Command: " << std::quoted(cmd) << "\\n";\n if (cmd == "quit") {\n Stop();\n return 1;\n }\n std::cerr << "Unknown command (\\"quit\\" to exit)" << std::endl;\n return 0;\n }\n\n std::istream& operator>>(std::istream& is, Server& srv) {\n std::string str;\n is >> str;\n if (srv.Command(str)) {\n is.setstate(std::ios::badbit);\n }\n return is;\n }\nRun Code Online (Sandbox Code Playgroud)\n并在main:
while (std::cin >> srv) { }\nRun Code Online (Sandbox Code Playgroud)\n文件Acceptor.h
#ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_ACCEPTOR_H\n #define _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_ACCEPTOR_H\n\n #include "Service.h"\n class Acceptor {\n public:\n template <typename Executor>\n Acceptor(Executor ex, unsigned short port_num) : mAcceptor(make_strand(ex), {{}, port_num}) {}\n void Start();\n void Stop();\n\n private:\n tcp::acceptor mAcceptor;\n void InitAccept();\n void OnAccept(error_code ec, tcp::socket&& sock);\n };\n\n #endif\nRun Code Online (Sandbox Code Playgroud)\n文件Common.h
#pragma once\n #include <boost/asio.hpp>\n #include <memory>\n #include <thread>\n #include <atomic>\n\n namespace asio = boost::asio;\n using boost::system::error_code;\n using asio::ip::tcp;\nRun Code Online (Sandbox Code Playgroud)\n文件Server.h
#ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVER_H\n #define _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVER_H\n\n #include "Acceptor.h"\n class Server {\n public:\n explicit Server(unsigned short port_num);\n void Start();\n void Stop();\n int Command(std::string const& str);\n\n private:\n asio::thread_pool mio;\n Acceptor mAcceptor;\n };\n\n std::istream& operator>>(std::istream& is, Server& srv);\n\n #endif\nRun Code Online (Sandbox Code Playgroud)\n文件Service.h
#ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVICE_H\n #define _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVICE_H\n\n #include "Common.h"\n #include <iostream>\n class Service : public std::enable_shared_from_this<Service> {\n public:\n explicit Service(tcp::socket&& sock);\n void StartHandling();\n\n private:\n void OnRequestReceived(error_code ec, std::size_t bytes_transferred);\n std::string mRequest, mReponse;\n tcp::socket mSock;\n void OnReponseSent(error_code ec, size_t bytes_transferred);\n\n std::string ProcessRequest(std::string_view request);\n };\n\n #endif\nRun Code Online (Sandbox Code Playgroud)\n文件Acceptor.cpp
#include "Acceptor.h"\n #include <utility>\n\n void Acceptor::Start() {\n mAcceptor.listen();\n InitAccept();\n }\n\n void Acceptor::Stop() {\n // be thread safe\n post(mAcceptor.get_executor(), [this] { mAcceptor.cancel(); });\n }\n\n void Acceptor::InitAccept() {\n mAcceptor.async_accept(\n make_strand(mAcceptor.get_executor()),\n [this](error_code error, tcp::socket&& sock) { OnAccept(error, std::move(sock)); });\n }\n\n void Acceptor::OnAccept(error_code ec, tcp::socket&& sock) {\n if (!ec.failed()) {\n std::make_shared<Service>(std::move(sock))->StartHandling();\n InitAccept();\n } else {\n std::cout << "OnAccept: " << ec.message() << "\\n";\n }\n }\nRun Code Online (Sandbox Code Playgroud)\n文件main.cpp
#include "Server.h"\n #include <iostream>\n\n int main() {\n if (uint16_t port_num; std::cin >> port_num) {\n try {\n Server srv(port_num);\n std::cout << "Port - " << port_num << "\\nServer start\\n";\n srv.Start();\n\n while (std::cin >> srv) { }\n } catch (boost::system::system_error const& e) {\n std::cout << "Error " << e.code().message() << "\\n";\n }\n } else {\n std::cerr << "Invalid input (port number required)\\n";\n }\n }\nRun Code Online (Sandbox Code Playgroud)\n文件Server.cpp
#include "Server.h"\n #include <iomanip>\n\n Server::Server(unsigned short port_num)\n : mAcceptor(make_strand(mio), port_num) {}\n\n void Server::Start() { mAcceptor.Start(); }\n void Server::Stop() { mAcceptor.Stop(); }\n\n int Server::Command(std::string const& cmd) {\n std::cout << "Command: " << std::quoted(cmd) << "\\n";\n if (cmd == "quit") {\n Stop();\n return 1;\n }\n std::cerr << "Unknown command (\\"quit\\" to exit)" << std::endl;\n return 0;\n }\n\n std::istream& operator>>(std::istream& is, Server& srv) {\n std::string str;\n is >> str;\n if (srv.Command(str)) {\n is.setstate(std::ios::badbit);\n }\n return is;\n }\nRun Code Online (Sandbox Code Playgroud)\n文件Service.cpp
#include "Service.h"\n #include <utility>\n #include <iomanip>\n\n Service::Service(tcp::socket&& sock)\n : mSock(std::move(sock)) {}\n\n void Service::StartHandling() {\n asio::async_read_until(\n mSock, asio::dynamic_buffer(mRequest), \'\\n\',\n [this, self = shared_from_this()](error_code ec, std::size_t bytes_transferred) {\n OnRequestReceived(ec, bytes_transferred);\n });\n }\n\n void Service::OnRequestReceived(error_code ec, std::size_t bytes_transferred) {\n if (ec) {\n std::cout << "OnRequestReceived: " << ec.message() << "\\n";\n return;\n }\n\n std::string_view view = mRequest;\n mReponse = ProcessRequest(view.substr(0, bytes_transferred));\n\n asio::async_write(\n mSock, asio::buffer(mReponse),\n [this, self = shared_from_this()](error_code ec, std::size_t bytes_transferred) {\n OnReponseSent(ec, bytes_transferred);\n });\n }\n\n void Service::OnReponseSent(error_code ec, std::size_t /*bytes_transferred*/) {\n if (ec) {\n std::cout << "OnReponseSent: " << ec.message() << "\\n";\n }\n }\n\n #include <boost/iostreams/device/array.hpp>\n #include <boost/iostreams/stream_buffer.hpp>\n\n std::string Service::ProcessRequest(std::string_view request) {\n //std::cerr << "TRACE: " << std::quoted(request) << "\\n";\n assert(request.back() == \'\\n\');\n boost::iostreams::stream_buffer<boost::iostreams::array_source> buf(\n request.data(), request.size());\n\n std::istream input(&buf);\n std::string reponse;\n std::getline(input, reponse);\n return reponse + \'\\n\';\n }\nRun Code Online (Sandbox Code Playgroud)\n例如,当使用2323稍后的quit命令运行时:
# (echo 2323; sleep 30; echo quit) | ./sotest\nPort - 2323\nServer start\nCommand: "quit"\nOnAccept: Operation canceled\nRun Code Online (Sandbox Code Playgroud)\n它确实正确接受多个连接:
\n# for a in {1..10}; do printf "Message with random data $RANDOM\\n" | nc localhost 2323; done\nMessage with random data 8002\nMessage with random data 28046\nMessage with random data 17943\nMessage with random data 17845\nMessage with random data 10832\nMessage with random data 20049\nMessage with random data 27593\nMessage with random data 18979\nMessage with random data 2773\nMessage with random data 31159\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
1267 次 |
| 最近记录: |