]>
Commit | Line | Data |
---|---|---|
1 | // | |
2 | // server.cpp | |
3 | // ~~~~~~~~~~ | |
4 | // | |
5 | // Copyright (c) 2003-2023 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: | |
31 | handler_memory() | |
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 | ||
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 | ||
120 | class session | |
121 | : public boost::enable_shared_from_this<session> | |
122 | { | |
123 | public: | |
124 | session(boost::asio::io_context& io_context) | |
125 | : socket_(io_context) | |
126 | { | |
127 | } | |
128 | ||
129 | tcp::socket& socket() | |
130 | { | |
131 | return socket_; | |
132 | } | |
133 | ||
134 | void start() | |
135 | { | |
136 | socket_.async_read_some(boost::asio::buffer(data_), | |
137 | boost::asio::bind_allocator( | |
138 | handler_allocator<int>(handler_memory_), | |
139 | boost::bind(&session::handle_read, | |
140 | shared_from_this(), | |
141 | boost::asio::placeholders::error, | |
142 | boost::asio::placeholders::bytes_transferred))); | |
143 | } | |
144 | ||
145 | void handle_read(const boost::system::error_code& error, | |
146 | size_t bytes_transferred) | |
147 | { | |
148 | if (!error) | |
149 | { | |
150 | boost::asio::async_write(socket_, | |
151 | boost::asio::buffer(data_, bytes_transferred), | |
152 | boost::asio::bind_allocator( | |
153 | handler_allocator<int>(handler_memory_), | |
154 | boost::bind(&session::handle_write, | |
155 | shared_from_this(), | |
156 | boost::asio::placeholders::error))); | |
157 | } | |
158 | } | |
159 | ||
160 | void handle_write(const boost::system::error_code& error) | |
161 | { | |
162 | if (!error) | |
163 | { | |
164 | socket_.async_read_some(boost::asio::buffer(data_), | |
165 | boost::asio::bind_allocator( | |
166 | handler_allocator<int>(handler_memory_), | |
167 | boost::bind(&session::handle_read, | |
168 | shared_from_this(), | |
169 | boost::asio::placeholders::error, | |
170 | boost::asio::placeholders::bytes_transferred))); | |
171 | } | |
172 | } | |
173 | ||
174 | private: | |
175 | // The socket used to communicate with the client. | |
176 | tcp::socket socket_; | |
177 | ||
178 | // Buffer used to store data received from the client. | |
179 | boost::array<char, 1024> data_; | |
180 | ||
181 | // The memory to use for handler-based custom memory allocation. | |
182 | handler_memory handler_memory_; | |
183 | }; | |
184 | ||
185 | typedef boost::shared_ptr<session> session_ptr; | |
186 | ||
187 | class server | |
188 | { | |
189 | public: | |
190 | server(boost::asio::io_context& io_context, short port) | |
191 | : io_context_(io_context), | |
192 | acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) | |
193 | { | |
194 | session_ptr new_session(new session(io_context_)); | |
195 | acceptor_.async_accept(new_session->socket(), | |
196 | boost::bind(&server::handle_accept, this, new_session, | |
197 | boost::asio::placeholders::error)); | |
198 | } | |
199 | ||
200 | void handle_accept(session_ptr new_session, | |
201 | const boost::system::error_code& error) | |
202 | { | |
203 | if (!error) | |
204 | { | |
205 | new_session->start(); | |
206 | } | |
207 | ||
208 | new_session.reset(new session(io_context_)); | |
209 | acceptor_.async_accept(new_session->socket(), | |
210 | boost::bind(&server::handle_accept, this, new_session, | |
211 | boost::asio::placeholders::error)); | |
212 | } | |
213 | ||
214 | private: | |
215 | boost::asio::io_context& io_context_; | |
216 | tcp::acceptor acceptor_; | |
217 | }; | |
218 | ||
219 | int main(int argc, char* argv[]) | |
220 | { | |
221 | try | |
222 | { | |
223 | if (argc != 2) | |
224 | { | |
225 | std::cerr << "Usage: server <port>\n"; | |
226 | return 1; | |
227 | } | |
228 | ||
229 | boost::asio::io_context io_context; | |
230 | ||
231 | using namespace std; // For atoi. | |
232 | server s(io_context, atoi(argv[1])); | |
233 | ||
234 | io_context.run(); | |
235 | } | |
236 | catch (std::exception& e) | |
237 | { | |
238 | std::cerr << "Exception: " << e.what() << "\n"; | |
239 | } | |
240 | ||
241 | return 0; | |
242 | } |