1//
2// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7// Official repository: https://github.com/boostorg/beast
8//
9
10//------------------------------------------------------------------------------
11//
12// Example: WebSocket client, asynchronous
13//
14//------------------------------------------------------------------------------
15
16#include <boost/beast/core.hpp>
17#include <boost/beast/websocket.hpp>
18#include <boost/asio/strand.hpp>
19#include <cstdlib>
20#include <functional>
21#include <iostream>
22#include <memory>
23#include <string>
24
25namespace beast = boost::beast; // from <boost/beast.hpp>
26namespace http = beast::http; // from <boost/beast/http.hpp>
27namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
28namespace net = boost::asio; // from <boost/asio.hpp>
29using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
30
31//------------------------------------------------------------------------------
32
33// Report a failure
34void
35fail(beast::error_code ec, char const* what)
36{
37 std::cerr << what << ": " << ec.message() << "\n";
38}
39
40// Sends a WebSocket message and prints the response
41class session : public std::enable_shared_from_this<session>
42{
43 tcp::resolver resolver_;
44 websocket::stream<beast::tcp_stream> ws_;
45 beast::flat_buffer buffer_;
46 std::string host_;
47 std::string text_;
48
49public:
50 // Resolver and socket require an io_context
51 explicit
52 session(net::io_context& ioc)
53 : resolver_(net::make_strand(ctx&: ioc))
54 , ws_(net::make_strand(ctx&: ioc))
55 {
56 }
57
58 // Start the asynchronous operation
59 void
60 run(
61 char const* host,
62 char const* port,
63 char const* text)
64 {
65 // Save these for later
66 host_ = host;
67 text_ = text;
68
69 // Look up the domain name
70 resolver_.async_resolve(
71 host,
72 service: port,
73 token: beast::bind_front_handler(
74 handler: &session::on_resolve,
75 args: shared_from_this()));
76 }
77
78 void
79 on_resolve(
80 beast::error_code ec,
81 tcp::resolver::results_type results)
82 {
83 if(ec)
84 return fail(ec, what: "resolve");
85
86 // Set the timeout for the operation
87 beast::get_lowest_layer(t&: ws_).expires_after(expiry_time: std::chrono::seconds(30));
88
89 // Make the connection on the IP address we get from a lookup
90 beast::get_lowest_layer(t&: ws_).async_connect(
91 endpoints: results,
92 handler: beast::bind_front_handler(
93 handler: &session::on_connect,
94 args: shared_from_this()));
95 }
96
97 void
98 on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type ep)
99 {
100 if(ec)
101 return fail(ec, what: "connect");
102
103 // Turn off the timeout on the tcp_stream, because
104 // the websocket stream has its own timeout system.
105 beast::get_lowest_layer(t&: ws_).expires_never();
106
107 // Set suggested timeout settings for the websocket
108 ws_.set_option(
109 websocket::stream_base::timeout::suggested(
110 role: beast::role_type::client));
111
112 // Set a decorator to change the User-Agent of the handshake
113 ws_.set_option(websocket::stream_base::decorator(
114 [](websocket::request_type& req)
115 {
116 req.set(name: http::field::user_agent,
117 value: std::string(BOOST_BEAST_VERSION_STRING) +
118 " websocket-client-async");
119 }));
120
121 // Update the host_ string. This will provide the value of the
122 // Host HTTP header during the WebSocket handshake.
123 // See https://tools.ietf.org/html/rfc7230#section-5.4
124 host_ += ':' + std::to_string(val: ep.port());
125
126 // Perform the websocket handshake
127 ws_.async_handshake(host: host_, target: "/",
128 handler: beast::bind_front_handler(
129 handler: &session::on_handshake,
130 args: shared_from_this()));
131 }
132
133 void
134 on_handshake(beast::error_code ec)
135 {
136 if(ec)
137 return fail(ec, what: "handshake");
138
139 // Send the message
140 ws_.async_write(
141 bs: net::buffer(data&: text_),
142 handler: beast::bind_front_handler(
143 handler: &session::on_write,
144 args: shared_from_this()));
145 }
146
147 void
148 on_write(
149 beast::error_code ec,
150 std::size_t bytes_transferred)
151 {
152 boost::ignore_unused(bytes_transferred);
153
154 if(ec)
155 return fail(ec, what: "write");
156
157 // Read a message into our buffer
158 ws_.async_read(
159 buffer&: buffer_,
160 handler: beast::bind_front_handler(
161 handler: &session::on_read,
162 args: shared_from_this()));
163 }
164
165 void
166 on_read(
167 beast::error_code ec,
168 std::size_t bytes_transferred)
169 {
170 boost::ignore_unused(bytes_transferred);
171
172 if(ec)
173 return fail(ec, what: "read");
174
175 // Close the WebSocket connection
176 ws_.async_close(cr: websocket::close_code::normal,
177 handler: beast::bind_front_handler(
178 handler: &session::on_close,
179 args: shared_from_this()));
180 }
181
182 void
183 on_close(beast::error_code ec)
184 {
185 if(ec)
186 return fail(ec, what: "close");
187
188 // If we get here then the connection is closed gracefully
189
190 // The make_printable() function helps print a ConstBufferSequence
191 std::cout << beast::make_printable(buffers: buffer_.data()) << std::endl;
192 }
193};
194
195//------------------------------------------------------------------------------
196
197int main(int argc, char** argv)
198{
199 // Check command line arguments.
200 if(argc != 4)
201 {
202 std::cerr <<
203 "Usage: websocket-client-async <host> <port> <text>\n" <<
204 "Example:\n" <<
205 " websocket-client-async echo.websocket.org 80 \"Hello, world!\"\n";
206 return EXIT_FAILURE;
207 }
208 auto const host = argv[1];
209 auto const port = argv[2];
210 auto const text = argv[3];
211
212 // The io_context is required for all I/O
213 net::io_context ioc;
214
215 // Launch the asynchronous operation
216 std::make_shared<session>(args&: ioc)->run(host, port, text);
217
218 // Run the I/O service. The call will return when
219 // the socket is closed.
220 ioc.run();
221
222 return EXIT_SUCCESS;
223}
224

source code of boost/libs/beast/example/websocket/client/async/websocket_client_async.cpp