]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // server.cpp | |
3 | // ~~~~~~~~~~ | |
4 | // | |
b32b8144 | 5 | // Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
7c673cae FG |
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.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. | |
b32b8144 | 27 | class handler_memory |
7c673cae FG |
28 | : private boost::noncopyable |
29 | { | |
30 | public: | |
b32b8144 | 31 | handler_memory() |
7c673cae FG |
32 | : in_use_(false) |
33 | { | |
34 | } | |
35 | ||
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 | ||
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 | ||
b32b8144 FG |
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 | ||
78 | explicit handler_allocator(handler_memory& mem) | |
79 | : memory_(mem) | |
80 | { | |
81 | } | |
82 | ||
83 | template <typename U> | |
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 | ||
95 | bool operator==(const handler_allocator& other) const | |
96 | { | |
97 | return &memory_ == &other.memory_; | |
98 | } | |
99 | ||
100 | bool operator!=(const handler_allocator& other) const | |
101 | { | |
102 | return &memory_ != &other.memory_; | |
103 | } | |
104 | ||
105 | T* allocate(std::size_t n) const | |
106 | { | |
107 | return static_cast<T*>(memory_.allocate(sizeof(T) * n)); | |
108 | } | |
109 | ||
110 | 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 | ||
7c673cae | 120 | // Wrapper class template for handler objects to allow handler memory |
b32b8144 FG |
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. | |
7c673cae FG |
124 | template <typename Handler> |
125 | class custom_alloc_handler | |
126 | { | |
127 | public: | |
b32b8144 FG |
128 | typedef handler_allocator<Handler> allocator_type; |
129 | ||
130 | custom_alloc_handler(handler_memory& m, Handler h) | |
131 | : memory_(m), | |
7c673cae FG |
132 | handler_(h) |
133 | { | |
134 | } | |
135 | ||
b32b8144 FG |
136 | allocator_type get_allocator() const |
137 | { | |
138 | return allocator_type(memory_); | |
139 | } | |
140 | ||
7c673cae FG |
141 | template <typename Arg1> |
142 | void operator()(Arg1 arg1) | |
143 | { | |
144 | handler_(arg1); | |
145 | } | |
146 | ||
147 | template <typename Arg1, typename Arg2> | |
148 | void operator()(Arg1 arg1, Arg2 arg2) | |
149 | { | |
150 | handler_(arg1, arg2); | |
151 | } | |
152 | ||
7c673cae | 153 | private: |
b32b8144 | 154 | handler_memory& memory_; |
7c673cae FG |
155 | Handler handler_; |
156 | }; | |
157 | ||
158 | // Helper function to wrap a handler object to add custom allocation. | |
159 | template <typename Handler> | |
160 | inline custom_alloc_handler<Handler> make_custom_alloc_handler( | |
b32b8144 | 161 | handler_memory& m, Handler h) |
7c673cae | 162 | { |
b32b8144 | 163 | return custom_alloc_handler<Handler>(m, h); |
7c673cae FG |
164 | } |
165 | ||
166 | class session | |
167 | : public boost::enable_shared_from_this<session> | |
168 | { | |
169 | public: | |
b32b8144 FG |
170 | session(boost::asio::io_context& io_context) |
171 | : socket_(io_context) | |
7c673cae FG |
172 | { |
173 | } | |
174 | ||
175 | tcp::socket& socket() | |
176 | { | |
177 | return socket_; | |
178 | } | |
179 | ||
180 | void start() | |
181 | { | |
182 | socket_.async_read_some(boost::asio::buffer(data_), | |
b32b8144 | 183 | make_custom_alloc_handler(handler_memory_, |
7c673cae FG |
184 | boost::bind(&session::handle_read, |
185 | shared_from_this(), | |
186 | boost::asio::placeholders::error, | |
187 | boost::asio::placeholders::bytes_transferred))); | |
188 | } | |
189 | ||
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), | |
b32b8144 | 197 | make_custom_alloc_handler(handler_memory_, |
7c673cae FG |
198 | boost::bind(&session::handle_write, |
199 | shared_from_this(), | |
200 | boost::asio::placeholders::error))); | |
201 | } | |
202 | } | |
203 | ||
204 | void handle_write(const boost::system::error_code& error) | |
205 | { | |
206 | if (!error) | |
207 | { | |
208 | socket_.async_read_some(boost::asio::buffer(data_), | |
b32b8144 | 209 | make_custom_alloc_handler(handler_memory_, |
7c673cae FG |
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 | ||
b32b8144 FG |
224 | // The memory to use for handler-based custom memory allocation. |
225 | handler_memory handler_memory_; | |
7c673cae FG |
226 | }; |
227 | ||
228 | typedef boost::shared_ptr<session> session_ptr; | |
229 | ||
230 | class server | |
231 | { | |
232 | public: | |
b32b8144 FG |
233 | server(boost::asio::io_context& io_context, short port) |
234 | : io_context_(io_context), | |
235 | acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) | |
7c673cae | 236 | { |
b32b8144 | 237 | session_ptr new_session(new session(io_context_)); |
7c673cae FG |
238 | acceptor_.async_accept(new_session->socket(), |
239 | boost::bind(&server::handle_accept, this, new_session, | |
240 | boost::asio::placeholders::error)); | |
241 | } | |
242 | ||
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 | ||
b32b8144 | 251 | new_session.reset(new session(io_context_)); |
7c673cae FG |
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: | |
b32b8144 | 258 | boost::asio::io_context& io_context_; |
7c673cae FG |
259 | tcp::acceptor acceptor_; |
260 | }; | |
261 | ||
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 | ||
b32b8144 | 272 | boost::asio::io_context io_context; |
7c673cae FG |
273 | |
274 | using namespace std; // For atoi. | |
b32b8144 | 275 | server s(io_context, atoi(argv[1])); |
7c673cae | 276 | |
b32b8144 | 277 | io_context.run(); |
7c673cae FG |
278 | } |
279 | catch (std::exception& e) | |
280 | { | |
281 | std::cerr << "Exception: " << e.what() << "\n"; | |
282 | } | |
283 | ||
284 | return 0; | |
285 | } |