websocket_client_async.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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. // Example: WebSocket client, asynchronous
  12. //
  13. //------------------------------------------------------------------------------
  14. #include <boost/beast/core.hpp>
  15. #include <boost/beast/websocket.hpp>
  16. #include <boost/asio/strand.hpp>
  17. #include <cstdlib>
  18. #include <functional>
  19. #include <iostream>
  20. #include <memory>
  21. #include <string>
  22. namespace beast = boost::beast; // from <boost/beast.hpp>
  23. namespace http = beast::http; // from <boost/beast/http.hpp>
  24. namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
  25. namespace net = boost::asio; // from <boost/asio.hpp>
  26. using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
  27. //------------------------------------------------------------------------------
  28. // Report a failure
  29. void
  30. fail(beast::error_code ec, char const* what)
  31. {
  32. std::cerr << what << ": " << ec.message() << "\n";
  33. }
  34. // Sends a WebSocket message and prints the response
  35. class session : public std::enable_shared_from_this<session>
  36. {
  37. tcp::resolver resolver_;
  38. websocket::stream<beast::tcp_stream> ws_;
  39. beast::flat_buffer buffer_;
  40. std::string host_;
  41. std::string text_;
  42. public:
  43. // Resolver and socket require an io_context
  44. explicit
  45. session(net::io_context& ioc)
  46. : resolver_(net::make_strand(ioc))
  47. , ws_(net::make_strand(ioc))
  48. {
  49. }
  50. // Start the asynchronous operation
  51. void
  52. run(
  53. char const* host,
  54. char const* port,
  55. char const* text)
  56. {
  57. // Save these for later
  58. host_ = host;
  59. text_ = text;
  60. // Look up the domain name
  61. resolver_.async_resolve(
  62. host,
  63. port,
  64. beast::bind_front_handler(
  65. &session::on_resolve,
  66. shared_from_this()));
  67. }
  68. void
  69. on_resolve(
  70. beast::error_code ec,
  71. tcp::resolver::results_type results)
  72. {
  73. if(ec)
  74. return fail(ec, "resolve");
  75. // Set the timeout for the operation
  76. beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));
  77. // Make the connection on the IP address we get from a lookup
  78. beast::get_lowest_layer(ws_).async_connect(
  79. results,
  80. beast::bind_front_handler(
  81. &session::on_connect,
  82. shared_from_this()));
  83. }
  84. void
  85. on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
  86. {
  87. if(ec)
  88. return fail(ec, "connect");
  89. // Turn off the timeout on the tcp_stream, because
  90. // the websocket stream has its own timeout system.
  91. beast::get_lowest_layer(ws_).expires_never();
  92. // Set suggested timeout settings for the websocket
  93. ws_.set_option(
  94. websocket::stream_base::timeout::suggested(
  95. beast::role_type::client));
  96. // Set a decorator to change the User-Agent of the handshake
  97. ws_.set_option(websocket::stream_base::decorator(
  98. [](websocket::request_type& req)
  99. {
  100. req.set(http::field::user_agent,
  101. std::string(BOOST_BEAST_VERSION_STRING) +
  102. " websocket-client-async");
  103. }));
  104. // Perform the websocket handshake
  105. ws_.async_handshake(host_, "/",
  106. beast::bind_front_handler(
  107. &session::on_handshake,
  108. shared_from_this()));
  109. }
  110. void
  111. on_handshake(beast::error_code ec)
  112. {
  113. if(ec)
  114. return fail(ec, "handshake");
  115. // Send the message
  116. ws_.async_write(
  117. net::buffer(text_),
  118. beast::bind_front_handler(
  119. &session::on_write,
  120. shared_from_this()));
  121. }
  122. void
  123. on_write(
  124. beast::error_code ec,
  125. std::size_t bytes_transferred)
  126. {
  127. boost::ignore_unused(bytes_transferred);
  128. if(ec)
  129. return fail(ec, "write");
  130. // Read a message into our buffer
  131. ws_.async_read(
  132. buffer_,
  133. beast::bind_front_handler(
  134. &session::on_read,
  135. shared_from_this()));
  136. }
  137. void
  138. on_read(
  139. beast::error_code ec,
  140. std::size_t bytes_transferred)
  141. {
  142. boost::ignore_unused(bytes_transferred);
  143. if(ec)
  144. return fail(ec, "read");
  145. // Close the WebSocket connection
  146. ws_.async_close(websocket::close_code::normal,
  147. beast::bind_front_handler(
  148. &session::on_close,
  149. shared_from_this()));
  150. }
  151. void
  152. on_close(beast::error_code ec)
  153. {
  154. if(ec)
  155. return fail(ec, "close");
  156. // If we get here then the connection is closed gracefully
  157. // The make_printable() function helps print a ConstBufferSequence
  158. std::cout << beast::make_printable(buffer_.data()) << std::endl;
  159. }
  160. };
  161. //------------------------------------------------------------------------------
  162. int main(int argc, char** argv)
  163. {
  164. // Check command line arguments.
  165. if(argc != 4)
  166. {
  167. std::cerr <<
  168. "Usage: websocket-client-async <host> <port> <text>\n" <<
  169. "Example:\n" <<
  170. " websocket-client-async echo.websocket.org 80 \"Hello, world!\"\n";
  171. return EXIT_FAILURE;
  172. }
  173. auto const host = argv[1];
  174. auto const port = argv[2];
  175. auto const text = argv[3];
  176. // The io_context is required for all I/O
  177. net::io_context ioc;
  178. // Launch the asynchronous operation
  179. std::make_shared<session>(ioc)->run(host, port, text);
  180. // Run the I/O service. The call will return when
  181. // the socket is closed.
  182. ioc.run();
  183. return EXIT_SUCCESS;
  184. }