• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // server.cpp
3 // ~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #include <cstdlib>
12 #include <iostream>
13 #include <boost/aligned_storage.hpp>
14 #include <boost/array.hpp>
15 #include <boost/bind/bind.hpp>
16 #include <boost/enable_shared_from_this.hpp>
17 #include <boost/noncopyable.hpp>
18 #include <boost/shared_ptr.hpp>
19 #include <boost/asio.hpp>
20 
21 using boost::asio::ip::tcp;
22 
23 // Class to manage the memory to be used for handler-based custom allocation.
24 // It contains a single block of memory which may be returned for allocation
25 // requests. If the memory is in use when an allocation request is made, the
26 // allocator delegates allocation to the global heap.
27 class handler_memory
28   : private boost::noncopyable
29 {
30 public:
handler_memory()31   handler_memory()
32     : in_use_(false)
33   {
34   }
35 
allocate(std::size_t size)36   void* allocate(std::size_t size)
37   {
38     if (!in_use_ && size < storage_.size)
39     {
40       in_use_ = true;
41       return storage_.address();
42     }
43     else
44     {
45       return ::operator new(size);
46     }
47   }
48 
deallocate(void * pointer)49   void deallocate(void* pointer)
50   {
51     if (pointer == storage_.address())
52     {
53       in_use_ = false;
54     }
55     else
56     {
57       ::operator delete(pointer);
58     }
59   }
60 
61 private:
62   // Storage space used for handler-based custom memory allocation.
63   boost::aligned_storage<1024> storage_;
64 
65   // Whether the handler-based custom allocation storage has been used.
66   bool in_use_;
67 };
68 
69 // The allocator to be associated with the handler objects. This allocator only
70 // needs to satisfy the C++11 minimal allocator requirements, plus rebind when
71 // targeting C++03.
72 template <typename T>
73 class handler_allocator
74 {
75 public:
76   typedef T value_type;
77 
handler_allocator(handler_memory & mem)78   explicit handler_allocator(handler_memory& mem)
79     : memory_(mem)
80   {
81   }
82 
83   template <typename U>
handler_allocator(const handler_allocator<U> & other)84   handler_allocator(const handler_allocator<U>& other)
85     : memory_(other.memory_)
86   {
87   }
88 
89   template <typename U>
90   struct rebind
91   {
92     typedef handler_allocator<U> other;
93   };
94 
operator ==(const handler_allocator & other) const95   bool operator==(const handler_allocator& other) const
96   {
97     return &memory_ == &other.memory_;
98   }
99 
operator !=(const handler_allocator & other) const100   bool operator!=(const handler_allocator& other) const
101   {
102     return &memory_ != &other.memory_;
103   }
104 
allocate(std::size_t n) const105   T* allocate(std::size_t n) const
106   {
107     return static_cast<T*>(memory_.allocate(sizeof(T) * n));
108   }
109 
deallocate(T * p,std::size_t) const110   void deallocate(T* p, std::size_t /*n*/) const
111   {
112     return memory_.deallocate(p);
113   }
114 
115 //private:
116   // The underlying memory.
117   handler_memory& memory_;
118 };
119 
120 // Wrapper class template for handler objects to allow handler memory
121 // allocation to be customised. The allocator_type typedef and get_allocator()
122 // member function are used by the asynchronous operations to obtain the
123 // allocator. Calls to operator() are forwarded to the encapsulated handler.
124 template <typename Handler>
125 class custom_alloc_handler
126 {
127 public:
128   typedef handler_allocator<Handler> allocator_type;
129 
custom_alloc_handler(handler_memory & m,Handler h)130   custom_alloc_handler(handler_memory& m, Handler h)
131     : memory_(m),
132       handler_(h)
133   {
134   }
135 
get_allocator() const136   allocator_type get_allocator() const
137   {
138     return allocator_type(memory_);
139   }
140 
141   template <typename Arg1>
operator ()(Arg1 arg1)142   void operator()(Arg1 arg1)
143   {
144     handler_(arg1);
145   }
146 
147   template <typename Arg1, typename Arg2>
operator ()(Arg1 arg1,Arg2 arg2)148   void operator()(Arg1 arg1, Arg2 arg2)
149   {
150     handler_(arg1, arg2);
151   }
152 
153 private:
154   handler_memory& memory_;
155   Handler handler_;
156 };
157 
158 // Helper function to wrap a handler object to add custom allocation.
159 template <typename Handler>
make_custom_alloc_handler(handler_memory & m,Handler h)160 inline custom_alloc_handler<Handler> make_custom_alloc_handler(
161     handler_memory& m, Handler h)
162 {
163   return custom_alloc_handler<Handler>(m, h);
164 }
165 
166 class session
167   : public boost::enable_shared_from_this<session>
168 {
169 public:
session(boost::asio::io_context & io_context)170   session(boost::asio::io_context& io_context)
171     : socket_(io_context)
172   {
173   }
174 
socket()175   tcp::socket& socket()
176   {
177     return socket_;
178   }
179 
start()180   void start()
181   {
182     socket_.async_read_some(boost::asio::buffer(data_),
183         make_custom_alloc_handler(handler_memory_,
184           boost::bind(&session::handle_read,
185             shared_from_this(),
186             boost::asio::placeholders::error,
187             boost::asio::placeholders::bytes_transferred)));
188   }
189 
handle_read(const boost::system::error_code & error,size_t bytes_transferred)190   void handle_read(const boost::system::error_code& error,
191       size_t bytes_transferred)
192   {
193     if (!error)
194     {
195       boost::asio::async_write(socket_,
196           boost::asio::buffer(data_, bytes_transferred),
197           make_custom_alloc_handler(handler_memory_,
198             boost::bind(&session::handle_write,
199               shared_from_this(),
200               boost::asio::placeholders::error)));
201     }
202   }
203 
handle_write(const boost::system::error_code & error)204   void handle_write(const boost::system::error_code& error)
205   {
206     if (!error)
207     {
208       socket_.async_read_some(boost::asio::buffer(data_),
209           make_custom_alloc_handler(handler_memory_,
210             boost::bind(&session::handle_read,
211               shared_from_this(),
212               boost::asio::placeholders::error,
213               boost::asio::placeholders::bytes_transferred)));
214     }
215   }
216 
217 private:
218   // The socket used to communicate with the client.
219   tcp::socket socket_;
220 
221   // Buffer used to store data received from the client.
222   boost::array<char, 1024> data_;
223 
224   // The memory to use for handler-based custom memory allocation.
225   handler_memory handler_memory_;
226 };
227 
228 typedef boost::shared_ptr<session> session_ptr;
229 
230 class server
231 {
232 public:
server(boost::asio::io_context & io_context,short port)233   server(boost::asio::io_context& io_context, short port)
234     : io_context_(io_context),
235       acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
236   {
237     session_ptr new_session(new session(io_context_));
238     acceptor_.async_accept(new_session->socket(),
239         boost::bind(&server::handle_accept, this, new_session,
240           boost::asio::placeholders::error));
241   }
242 
handle_accept(session_ptr new_session,const boost::system::error_code & error)243   void handle_accept(session_ptr new_session,
244       const boost::system::error_code& error)
245   {
246     if (!error)
247     {
248       new_session->start();
249     }
250 
251     new_session.reset(new session(io_context_));
252     acceptor_.async_accept(new_session->socket(),
253         boost::bind(&server::handle_accept, this, new_session,
254           boost::asio::placeholders::error));
255   }
256 
257 private:
258   boost::asio::io_context& io_context_;
259   tcp::acceptor acceptor_;
260 };
261 
main(int argc,char * argv[])262 int main(int argc, char* argv[])
263 {
264   try
265   {
266     if (argc != 2)
267     {
268       std::cerr << "Usage: server <port>\n";
269       return 1;
270     }
271 
272     boost::asio::io_context io_context;
273 
274     using namespace std; // For atoi.
275     server s(io_context, atoi(argv[1]));
276 
277     io_context.run();
278   }
279   catch (std::exception& e)
280   {
281     std::cerr << "Exception: " << e.what() << "\n";
282   }
283 
284   return 0;
285 }
286