2010-05-23 67 views
3
#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」關鍵字的用法。boost :: asio是否會產生過多的小堆分配,還是我錯了?

此外,當用一個發送例如100,000次數據的客戶端(1個連接)強調這個回顯服務器時,這個程序的內存使用率越來越高。

發生了什麼事?它會爲每次通話分配內存嗎?或者我錯了?詢問是因爲服務器應用程序分配不太合適,任何事情。我可以使用內存池來處理它嗎?

另一側問題

參見 「這 - >插座()關閉();」 ?
我希望它最後一次發送相同的函數,並且發生斷開連接錯誤。需要做一些清理工作。我怎麼做?

謝謝各位大師(:

回答

4

在此希望有人能有所貢獻......

在我的實驗

而且在升壓:: ASIO我已經決定,之後的服務器應用程序達我將在C++的'新'代碼處放置一個斷點,即在「new.cpp」@ function「void * __ CRTDECL operator new(size_t size)_THROW1(_STD bad_alloc)」中注意,我正在使用MSVC 2008。

使用上面的代碼,原始帖子

現在,BP正在連接一個客戶端。
分配完成(幾次)(如預期)(我知道它是因爲調試器在我設置的'new'關鍵字處停止),並且新客戶端現在已準備好發送/接收數據。
我將「hi」從客戶端發送到服務器。
'new'處的BP在handle_read()處被擊中。
來源是調用async_write()(我堆棧跟蹤與MSVC)。
命中F5(continue)會在'新'處生成另一個斷點 - 這次async_read_some()調用會生成它。

結論: 每個這樣的操作生成到「新」呼叫!!!!!!真正的服務器可能有最壞的情況!

因此,進一步尋找某種方式來使用某種內存池,所以這些「新」的調用將不存在使我想到的例子:「分配」。
路徑:「....... \ boost_1_43_0 \ libs \ asio \ example \ allocation \」。

這樣做的新代碼(下面寫)給了我歡呼結果;
調用async_write()和async_read_some()不會生成調用'new'。

到目前爲止,這很好,但說實話,我不能說我明白這是如何做到的;你可以看到分配器分解成幾塊,這讓我感到有些困惑。

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

shared_from_this是來自shared_ptr的類,它爲您提供了一個指向自己的弱指針。當你創建一個應該被共享指針使用的類,並且你想從它自己獲得一個shared_ptr的時候,你可以從這個類中派生出來。有關說明,請參閱http://www.boost.org/doc/libs/1_42_0/libs/smart_ptr/sp_techniques.html#from_this。 – Christopher 2010-05-27 17:29:43

+1

make處理程序代碼似乎創建了一個由綁定調用創建的函數對象的包裝。處理程序對象被複制,分配器通過引用傳遞。所有operator()(..)調用都被轉發給handler_對象。 alloc/free調用通過對allocator_的引用來處理。 – Christopher 2010-05-27 17:37:29

+0

嗨克里斯托弗,謝謝你花時間回答!我不能說我真的瞭解你,但底線是我怎樣才能讓它在運行時不使用'新'操作符?一個簡單的例子,可能使用:: std :: vector來合併對象將會非常糟糕! – Poni 2010-05-30 07:11:07

2

要回答你的第二個問題,你可以給它綁定到會話一個boost ::綁定參數:: handle_read和任何錯誤代碼,您使用io_service對象::崗位想。

+0

謝謝你,現在我知道了這個小竅門(: – Poni 2010-06-04 12:07:05

相關問題