boost::asio 是否进行了过多的小堆分配,还是我错了?

2024-04-05

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

class session
{
public:
    session(boost::asio::io_service& io_service)
        : socket_(io_service)
    {
    }

    tcp::socket& socket()
    {
        return socket_;
    }

    void start()
    {
        socket_.async_read_some(boost::asio::buffer(data_, max_length - 1),
            boost::bind(&session::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }

    void handle_read(const boost::system::error_code& error,
        size_t bytes_transferred)
    {
        if (!error)
        {
            data_[bytes_transferred] = '\0';
            if(NULL != strstr(data_, "quit"))
            {
                this->socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
                this->socket().close(); // how to make this dispatch "handle_read()" with a "disconnected" flag?
            }
            else
            {
                boost::asio::async_write(socket_,
                    boost::asio::buffer(data_, bytes_transferred),
                    boost::bind(&session::handle_write, this,
                    boost::asio::placeholders::error));

                socket_.async_read_some(boost::asio::buffer(data_, max_length - 1),
                    boost::bind(&session::handle_read, this,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));
            }
        }
        else
        {
            delete this;
        }
    }

    void handle_write(const boost::system::error_code& error)
    {
        if (!error)
        {
            //
        }
        else
        {
            delete this;
        }
    }

private:
    tcp::socket socket_;
    enum { max_length = 1024 };
    char data_[max_length];
};

class server
{
public:
    server(boost::asio::io_service& io_service, short port)
        : io_service_(io_service),
        acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
    {
        session* new_session = new session(io_service_);
        acceptor_.async_accept(new_session->socket(),
            boost::bind(&server::handle_accept, this, new_session,
            boost::asio::placeholders::error));
    }

    void handle_accept(session* new_session,
        const boost::system::error_code& error)
    {
        if (!error)
        {
            new_session->start();
            new_session = new session(io_service_);
            acceptor_.async_accept(new_session->socket(),
                boost::bind(&server::handle_accept, this, new_session,
                boost::asio::placeholders::error));
        }
        else
        {
            delete new_session;
        }
    }

private:
    boost::asio::io_service& io_service_;
    tcp::acceptor acceptor_;
};

int main(int argc, char* argv[])
{
    try
    {
        if (argc != 2)
        {
            std::cerr << "Usage: async_tcp_echo_server <port>\n";
            return 1;
        }

        boost::asio::io_service io_service;

        using namespace std; // For atoi.
        server s(io_service, atoi(argv[1]));

        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}

在尝试 boost::asio 时,我注意到在对 async_write()/async_read_some() 的调用中使用了 C++“new”关键字。

此外,当使用发送例如 100,000 次数据的客户端(1 个连接)对该 echo 服务器施加压力时,该程序的内存使用量会变得越来越高。

这是怎么回事?它会为每次调用分配内存吗?还是我错了?询问是因为服务器应用程序分配任何东西似乎都不正确。我可以用内存池来处理它吗?

另一个附带问题:

请参阅“this->socket().close();” ?
我希望它,正如其右侧的评论所说,最后一次调度相同的函数,并出现断开连接错误。需要它来做一些清理工作。我怎么做?

谢谢各位高手(:


希望有人能贡献一些东西......

进一步在 boost::asio 的实验中,我决定在服务器应用程序启动并运行后,我将在 C++ 的“新”代码处放置一个断点,即在“new.cpp”@ function“void *__CRTDECL 运算符”新的(size_t大小)_THROW1(_STD bad_alloc)”。请注意,我使用的是 MSVC 2008。

使用上面的原始帖子的代码:

现在 BP 已打开,我正在连接一个客户端。
分配已完成(几次)(如预期)(我知道这一点是因为调试器在我设置的“new”关键字处停止)并且新客户端现在已准备好发送/接收数据。
我从客户端向服务器发送“hi”。
'new'处的BP在handle_read()处被命中。
其来源是对 async_write() 的调用(我使用 MSVC 进行堆栈跟踪)。
按 F5(继续)会在“new”处生成另一个断点 - 这次是 async_read_some() 调用生成的。

结论:每个这样的操作都会生成对“new”的调用!!!!!!真实服务器可能出现的最坏情况!

因此,进一步寻找某种方法来使用某种内存池,这样这些“新”调用就不会存在,这让我想到了这个例子:“分配”。
它的路径:“......\boost_1_43_0\libs\asio\example\allocation\”。

对这个新代码(写在下面)做同样的事情给了我欢呼的结果;
调用 async_write() 和 async_read_some()不生成对“新”的呼唤。

到目前为止,一切都很好,但说实话,我不能说我完全理解这是如何完成的;如您所见,分配器被分解为几个部分,这让我有点困惑。

make_custom_alloc_handler() 什么是shared_from_this()?
我看到“会话”对象具有成员“handler_allocator allocator_”。每个“会话”对象是否都拥有这些对象的池?!我可以在“服务器”类级别拥有其中之一吗?它将被共享或其他什么?

“分配器”示例代码:

//
// server.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <cstdlib>
#include <iostream>
#include <boost/aligned_storage.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

// Class to manage the memory to be used for handler-based custom allocation.
// It contains a single block of memory which may be returned for allocation
// requests. If the memory is in use when an allocation request is made, the
// allocator delegates allocation to the global heap.
class handler_allocator
    : private boost::noncopyable
{
public:
    handler_allocator()
        : in_use_(false)
    {
    }

    void* allocate(std::size_t size)
    {
        if (!in_use_ && size < storage_.size)
        {
            in_use_ = true;
            return storage_.address();
        }
        else
        {
            return ::operator new(size);
        }
    }

    void deallocate(void* pointer)
    {
        if (pointer == storage_.address())
        {
            in_use_ = false;
        }
        else
        {
            ::operator delete(pointer);
        }
    }

private:
    // Storage space used for handler-based custom memory allocation.
    boost::aligned_storage<1024> storage_;

    // Whether the handler-based custom allocation storage has been used.
    bool in_use_;
};

// Wrapper class template for handler objects to allow handler memory
// allocation to be customised. Calls to operator() are forwarded to the
// encapsulated handler.
template <typename Handler>
class custom_alloc_handler
{
public:
    custom_alloc_handler(handler_allocator& a, Handler h)
        : allocator_(a),
        handler_(h)
    {
    }

    template <typename Arg1>
    void operator()(Arg1 arg1)
    {
        handler_(arg1);
    }

    template <typename Arg1, typename Arg2>
    void operator()(Arg1 arg1, Arg2 arg2)
    {
        handler_(arg1, arg2);
    }

    friend void* asio_handler_allocate(std::size_t size,
        custom_alloc_handler<Handler>* this_handler)
    {
        return this_handler->allocator_.allocate(size);
    }

    friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/,
        custom_alloc_handler<Handler>* this_handler)
    {
        this_handler->allocator_.deallocate(pointer);
    }

private:
    handler_allocator& allocator_;
    Handler handler_;
};

// Helper function to wrap a handler object to add custom allocation.
template <typename Handler>
inline custom_alloc_handler<Handler> make_custom_alloc_handler(
    handler_allocator& a, Handler h)
{
    return custom_alloc_handler<Handler>(a, h);
}

class session
    : public boost::enable_shared_from_this<session>
{
public:
    session(boost::asio::io_service& io_service)
        : socket_(io_service)
    {
    }

    tcp::socket& socket()
    {
        return socket_;
    }

    void start()
    {
        socket_.async_read_some(boost::asio::buffer(data_),
            make_custom_alloc_handler(allocator_,
            boost::bind(&session::handle_read,
            shared_from_this(),
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred)));
    }

    void handle_read(const boost::system::error_code& error,
        size_t bytes_transferred)
    {
        if (!error)
        {
            boost::asio::async_write(socket_,
                boost::asio::buffer(data_, bytes_transferred),
                make_custom_alloc_handler(allocator_, boost::bind(&session::handle_write, shared_from_this(), boost::asio::placeholders::error))
                );
        }
    }

    void handle_write(const boost::system::error_code& error)
    {
        if (!error)
        {
            socket_.async_read_some(boost::asio::buffer(data_),
                make_custom_alloc_handler(allocator_,
                boost::bind(&session::handle_read,
                shared_from_this(),
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred)));
        }
    }

private:
    // The socket used to communicate with the client.
    tcp::socket socket_;

    // Buffer used to store data received from the client.
    boost::array<char, 1024> data_;

    // The allocator to use for handler-based custom memory allocation.
    handler_allocator allocator_;
};

typedef boost::shared_ptr<session> session_ptr;

class server
{
public:
    server(boost::asio::io_service& io_service, short port)
        : io_service_(io_service),
        acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
    {
        session_ptr new_session(new session(io_service_));
        acceptor_.async_accept(new_session->socket(),
            boost::bind(&server::handle_accept, this, new_session,
            boost::asio::placeholders::error));
    }

    void handle_accept(session_ptr new_session,
        const boost::system::error_code& error)
    {
        if (!error)
        {
            new_session->start();
            new_session.reset(new session(io_service_));
            acceptor_.async_accept(new_session->socket(),
                boost::bind(&server::handle_accept, this, new_session,
                boost::asio::placeholders::error));
        }
    }

private:
    boost::asio::io_service& io_service_;
    tcp::acceptor acceptor_;
};

int main(int argc, char* argv[])
{
    try
    {
        if (argc != 2)
        {
            std::cerr << "Usage: server <port>\n";
            return 1;
        }

        boost::asio::io_service io_service;

        using namespace std; // For atoi.
        server s(io_service, atoi(argv[1]));

        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

boost::asio 是否进行了过多的小堆分配,还是我错了? 的相关文章

随机推荐

  • MKOverlayRenderer 在地图视图上显示 UIImage

    我正在尝试在 iOS 7 中的地图视图上显示图像 我将 MKOverlayRenderer 子类化如下 MapOverlayRenderer h import
  • Room 未向表中插入数据

    有趣的是 它有时会正确执行插入操作 我不知道为什么以及如何会发生这种情况 所以 我无法弄清楚我在哪里犯了错误 这是我的项目文件 1 SentFilesDao java Dao public interface SentFilesDao Qu
  • 在 VSCode 中通过缩进更改纯文本颜色

    我有兴趣在 VSCode 中进行讲座并在简单的文本文件中做笔记 因为我已经使用它进行编码 然而 对于我的一生 我无法弄清楚如何更改简单的文本颜色 我想要的是在一种缩进深度处具有一种文本颜色 例如 Notes a b Notes会是不同的颜色
  • 删除字符串中空格的最快方法

    我试图从数据库表中的字符串中获取由 分隔的多个电子邮件地址 但它也返回了空格 我想快速删除空格 以下代码确实删除了空格 但每当我尝试在字符串中获取大量电子邮件地址 例如 30000 然后尝试删除它们之间的空格时 它也会变得很慢 删除这些空格
  • 当 Repeater 的委托高度发生变化时调整 ColumnLayout

    我已经设置了一个名为测试1 qml包含以下内容 import QtQuick 2 6 import QtQuick Layouts 1 3 Rectangle width 800 height 1000 ColumnLayout ancho
  • UIViewController 中的扩展边缘属性是什么?

    我在论坛上读过有关此属性的内容 大多数人将此属性设置为 false 或在情节提要中取消选中它 我自己这样做是因为当我在视图中嵌入 UINavigation 控制器时 顶部栏将我的 UITextView 向下推 以便文本在底部开始编辑 取消选
  • C++:如何解决在未知点引起的首次异常?

    我正在处理的一个 C 项目在抛出第一次机会异常时终止 当我第一次尝试访问调试模式下的 Visual Studio 2008 时 会发生这种情况map
  • ActionBarSherlock 堆叠操作栏样式问题

    我不明白为什么堆叠ActionBar我已经实现了最左边的选项卡和屏幕边缘之间有一个间隙 最右侧选项卡的情况并非如此 我尝试通过设计样式来删除分隔线ActionBar 在尝试了一下样式后 我似乎能够覆盖 TabView 样式的属性 但不能覆盖
  • Android 打印堆栈跟踪

    如何在 android 中获得相当于 printStackTrace 的功能 我知道我可以通过将标签名称和字符串传递给日志记录方法来记录错误 但这只会给我一个空指针异常 如果我调用 e printStackTrace 这些数据会打印到哪里
  • LINQ:通过使不同类型的集合可转换/可比较来使用 .Except() ?

    给定两个不同类型的列表 是否可以使这些类型可以相互转换或相互比较 例如使用 TypeConverter 或类似的 以便 LINQ 查询可以比较它们 我在 SO 上看到过其他类似的问题 但没有任何迹象表明可以使类型之间可以相互转换来解决问题
  • Flutter - 在路由之间推送并获取价值

    如何将绿色字符串从 HomePage 页面发送到 ContaPage 页面 我想是这样的Navigator of context pushNamed conta green 但不知道如何进入页面conta the green string
  • mod_rewrite 到文本/类型/id

    我当前的代码是这样的 store php storeid 12 page 3 我想把它翻译成这样 mysite com roberts clothing store store 12 3 和这样的东西 profile php userid
  • 使用 df 获取可用磁盘空间以仅显示可用空间(以 kb 为单位)?

    我正在尝试输出文件系统上的可用磁盘空间量 example 如果我运行命令df k example我可以获得有关可用磁盘空间 以 kb 为单位 的良好信息 但只有通过人类并实际查看它 我需要获取这些数据并在 shell 脚本中的其他地方使用它
  • Android Studio NDK 链接器错误未定义对“cv::_OutputArray::_OutputArray(cv::Mat&)”的引用

    我正在开发一个使用 NDK 的 Android 项目 其中我使用了 opencv 来进行一些捕获和图像效果的工作 我的 Android mk 如下所示 LOCAL PATH call my dir include CLEAR VARS in
  • 以编程方式显示/隐藏虚拟键盘

    我需要将虚拟键盘保留在屏幕上 即使不使用也是如此 有没有办法强制其可见性 我试着打电话 requestFocus 在文本字段上 但仅当该项目通过鼠标或触摸事件获得焦点时键盘才会抬起 并在失去焦点时消失 我想改变这种默认行为 你应该使用这个
  • Android Studio Bumblebee 无法使用 Safeargs

    我最近将我的 android studio 更新为稳定的 Bumblebee 版本 我想在我的新 android 项目中使用带有安全参数的 Nav 控制器 但是随着 Gradle 插件已更改为 7 1 0 我很困惑在哪里添加安全参数的类路径
  • 如何使用编程代码动态生成 woocommerce 优惠券代码

    我想动态生成 woocommerce 优惠券代码 我的要求是 完成订单后 自动在管理端 woocommerce 优惠券代码列表中为特定产品生成一个优惠券代码 所以任何人知道我的上述需求解决方案 请帮助我 谢谢 克坦 你可以使用woocomm
  • 如何让 markdown.js 在 Delphi 的 TWebBrowser 中将 Markdown 文档显示为 HTML?

    如何让 markdown js 在 Delphi 的 TWebBrowser 中将 Markdown 文档显示为 HTML 给定包含 Markdown 的字符串的内容 如何让 markdown js 将该 markdown 转换为 HTML
  • 在泛型类中使用 equals

    我想要我的EqualTester调用重写的通用类equals 其泛型参数的方法 但它似乎调用Object equals反而 这是我的测试代码 import junit framework TestCase public class Equa
  • boost::asio 是否进行了过多的小堆分配,还是我错了?

    include