代码之家  ›  专栏  ›  技术社区  ›  Poni

ASIO进行过多的小堆分配还是我错了?

  •  4
  • Poni  · 技术社区  · 16 年前
    #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;
    }
    

    在对Booo::Asio进行实验时,我注意到在调用AsYcCnWrreWror()/AsYcCyRead()时,使用了C++的“新”关键字。

    此外,当使用发送了100000倍数据的客户机(1个连接)对这个echo服务器施加压力时,这个程序的内存使用率越来越高。

    发生什么事?它会为每个呼叫分配内存吗?还是我错了?询问,因为服务器应用程序分配的内容似乎不正确。我可以处理它吗,比如说使用内存池?

    另一个问题 :

    请参阅“this->socket().close();”?
    正如对它的评论所说,我希望它在最后一次调度同一个函数时出现断开错误。需要做些清理。我该怎么做?

    感谢所有的古鲁们(:

    2 回复  |  直到 16 年前
        1
  •  5
  •   Poni    16 年前

    希望有人能做出贡献…

    此外,在我在Booo::Asio的实验中,我决定,在服务器应用程序启动和运行之后,我会在C++的“新”代码中设置一个断点,即在“new .CPP”@函数“Vaul*y-CRTDECL操作符new(SiZeSt大小)注意,我使用的是MSVC 2008。

    使用上面的代码,原始邮件的 :

    既然BP接通了,我要连接一个客户。
    分配已完成(多次)(如预期的那样)(我知道这一点是因为调试器在我设置时停止在“new”关键字处),新客户机现在可以发送/接收数据了。
    我从客户机向服务器发送“嗨”。
    “new”处的bp在handle_read()处被点击。
    到的源是对async_write()的调用(我用msvc堆栈跟踪)。
    按F5(continue)会在“new”处生成另一个断点,这一次是由async_read_some()调用生成的。

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

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

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

    到目前为止,这很好,但老实说,我不能说我完全理解这是怎么做到的;正如您所看到的,分配器被分成了几个部分,这让我有点困惑。

    使_custom_alloc_handler()<---它到底做什么?
    从这个()共享了什么??
    我看到“session”对象具有成员“handler_allocator allocator_uu”。每个“会话”对象是否都包含这些对象的池?!我可以在“服务器”类级别上使用其中的一个,它是共享的还是其他的?

    “分配器”示例代码:

    //
    // 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;
    }
    
        2
  •  2
  •   Sam Miller    16 年前

    要回答第二个问题,您可以使用IO服务::Post,方法是给它一个绑定到session::handle_read的boost::bind参数以及您想要的任何错误代码。

    推荐文章