]>
Commit | Line | Data |
---|---|---|
1 | // | |
2 | // echo_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 <boost/asio/detached.hpp> | |
12 | #include <boost/asio/io_context.hpp> | |
13 | #include <boost/asio/ip/tcp.hpp> | |
14 | #include <boost/asio/spawn.hpp> | |
15 | #include <boost/asio/steady_timer.hpp> | |
16 | #include <boost/asio/write.hpp> | |
17 | #include <boost/bind/bind.hpp> | |
18 | #include <boost/shared_ptr.hpp> | |
19 | #include <boost/enable_shared_from_this.hpp> | |
20 | #include <iostream> | |
21 | ||
22 | using boost::asio::ip::tcp; | |
23 | ||
24 | class session : public boost::enable_shared_from_this<session> | |
25 | { | |
26 | public: | |
27 | explicit session(boost::asio::io_context& io_context) | |
28 | : strand_(boost::asio::make_strand(io_context)), | |
29 | socket_(io_context), | |
30 | timer_(io_context) | |
31 | { | |
32 | } | |
33 | ||
34 | tcp::socket& socket() | |
35 | { | |
36 | return socket_; | |
37 | } | |
38 | ||
39 | void go() | |
40 | { | |
41 | boost::asio::spawn(strand_, | |
42 | boost::bind(&session::echo, | |
43 | shared_from_this(), boost::placeholders::_1), | |
44 | boost::asio::detached_t()); | |
45 | boost::asio::spawn(strand_, | |
46 | boost::bind(&session::timeout, | |
47 | shared_from_this(), boost::placeholders::_1), | |
48 | boost::asio::detached_t()); | |
49 | } | |
50 | ||
51 | private: | |
52 | void echo(boost::asio::yield_context yield) | |
53 | { | |
54 | try | |
55 | { | |
56 | char data[128]; | |
57 | for (;;) | |
58 | { | |
59 | timer_.expires_after(boost::asio::chrono::seconds(10)); | |
60 | std::size_t n = socket_.async_read_some(boost::asio::buffer(data), yield); | |
61 | boost::asio::async_write(socket_, boost::asio::buffer(data, n), yield); | |
62 | } | |
63 | } | |
64 | catch (std::exception& e) | |
65 | { | |
66 | socket_.close(); | |
67 | timer_.cancel(); | |
68 | } | |
69 | } | |
70 | ||
71 | void timeout(boost::asio::yield_context yield) | |
72 | { | |
73 | while (socket_.is_open()) | |
74 | { | |
75 | boost::system::error_code ignored_ec; | |
76 | timer_.async_wait(yield[ignored_ec]); | |
77 | if (timer_.expiry() <= boost::asio::steady_timer::clock_type::now()) | |
78 | socket_.close(); | |
79 | } | |
80 | } | |
81 | ||
82 | boost::asio::strand<boost::asio::io_context::executor_type> strand_; | |
83 | tcp::socket socket_; | |
84 | boost::asio::steady_timer timer_; | |
85 | }; | |
86 | ||
87 | void do_accept(boost::asio::io_context& io_context, | |
88 | unsigned short port, boost::asio::yield_context yield) | |
89 | { | |
90 | tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), port)); | |
91 | ||
92 | for (;;) | |
93 | { | |
94 | boost::system::error_code ec; | |
95 | boost::shared_ptr<session> new_session(new session(io_context)); | |
96 | acceptor.async_accept(new_session->socket(), yield[ec]); | |
97 | if (!ec) new_session->go(); | |
98 | } | |
99 | } | |
100 | ||
101 | int main(int argc, char* argv[]) | |
102 | { | |
103 | try | |
104 | { | |
105 | if (argc != 2) | |
106 | { | |
107 | std::cerr << "Usage: echo_server <port>\n"; | |
108 | return 1; | |
109 | } | |
110 | ||
111 | boost::asio::io_context io_context; | |
112 | ||
113 | boost::asio::spawn(io_context, | |
114 | boost::bind(do_accept, boost::ref(io_context), | |
115 | atoi(argv[1]), boost::placeholders::_1), | |
116 | boost::asio::detached_t()); | |
117 | ||
118 | io_context.run(); | |
119 | } | |
120 | catch (std::exception& e) | |
121 | { | |
122 | std::cerr << "Exception: " << e.what() << "\n"; | |
123 | } | |
124 | ||
125 | return 0; | |
126 | } |