1
+ #include < iostream>
2
+ #include < string>
3
+ #include < variant>
4
+ #include < expected>
5
+ #include < chrono>
6
+ #include < algorithm>
7
+ #include < iomanip>
8
+
9
+ #include " ws_client/ws_client.hpp"
10
+ #include " ws_client/transport/builtin/TcpSocket.hpp"
11
+ #include " ws_client/transport/builtin/OpenSslSocket.hpp"
12
+ #include " ws_client/PermessageDeflate.hpp"
13
+
14
+ using namespace ws_client ;
15
+ using namespace std ::chrono;
16
+
17
+ expected<void , WSError> run ()
18
+ {
19
+ // parse URL
20
+ WS_TRY (url, URL::parse (" wss://fstream.binance.com/ws" ));
21
+
22
+ // websocketclient logger
23
+ ConsoleLogger<LogLevel::D> logger;
24
+
25
+ // resolve hostname
26
+ DnsResolver dns (&logger);
27
+ WS_TRY (dns_res, dns.resolve (url->host (), url->port_str ()));
28
+ AddressInfo& addr = (*dns_res)[0 ];
29
+
30
+ // create TCP socket
31
+ auto tcp = TcpSocket (&logger, std::move (addr));
32
+ WS_TRYV (tcp.init ());
33
+ WS_TRYV (tcp.set_SO_RCVBUF (1 * 1024 * 1024 )); // 1 MB
34
+
35
+ // SSL socket wrapper
36
+ OpenSslContext ctx (&logger);
37
+ WS_TRYV (ctx.init ());
38
+ WS_TRYV (ctx.set_default_verify_paths ());
39
+ auto ssl = OpenSslSocket (&logger, std::move (tcp), &ctx, url->host (), true );
40
+ WS_TRYV (ssl.init ());
41
+ WS_TRYV (ssl.connect (2s)); // 2 sec connect timeout
42
+
43
+ // websocket client
44
+ auto client = WebSocketClient (&logger, std::move (ssl));
45
+
46
+ // handshake handler
47
+ auto handshake = Handshake (&logger, *url);
48
+
49
+ // enable compression (permessage-deflate extension)
50
+ handshake.set_permessage_deflate ({
51
+ .logger = &logger,
52
+ .server_max_window_bits = 15 ,
53
+ .client_max_window_bits = 15 ,
54
+ .server_no_context_takeover = true ,
55
+ .client_no_context_takeover = true ,
56
+ .decompress_buffer_size = 2 * 1024 * 1024 , // 2 MB
57
+ .compress_buffer_size = 2 * 1024 * 1024 // 2 MB
58
+ });
59
+
60
+ // perform handshake
61
+ WS_TRYV (client.handshake (handshake, 5s)); // 5 sec timeout
62
+
63
+ // subscribe
64
+ std::string sub_msg = R"( {
65
+ "method": "SUBSCRIBE",
66
+ "params": ["aptusdt@aggTrade"],
67
+ "id": 1
68
+ })" ;
69
+ Message msg (MessageType::text, sub_msg);
70
+ WS_TRYV (client.send_message (msg, {.compress = false }));
71
+
72
+ // allocate message buffer with 4 KiB initial size and 1 MiB max size
73
+ WS_TRY (buffer, Buffer::create (4096 , 1 * 1024 * 1024 ));
74
+
75
+ while (true )
76
+ {
77
+ // wait for message for 3 sec
78
+ bool readable = false ;
79
+ do
80
+ {
81
+ WS_TRY (read_res, client.wait_message (3s));
82
+ if (!(readable = read_res.value ()))
83
+ logger.log <LogLevel::W>(" No message received within 3 sec, continue waiting..." );
84
+ } while (!readable);
85
+
86
+ // read message (only 1 sec timeout since we know socket is readable)
87
+ variant<Message, PingFrame, PongFrame, CloseFrame, WSError> var = //
88
+ client.read_message (*buffer, 1s);
89
+
90
+ if (auto msg = std::get_if<Message>(&var))
91
+ {
92
+ std::cout << " Message received: " << msg->to_string_view () << std::endl;
93
+ }
94
+ else if (auto ping_frame = std::get_if<PingFrame>(&var))
95
+ {
96
+ logger.log <LogLevel::D>(" Ping frame received" );
97
+ WS_TRYV (client.send_pong_frame (ping_frame->payload_bytes ()));
98
+ }
99
+ else if (std::get_if<PongFrame>(&var))
100
+ {
101
+ logger.log <LogLevel::D>(" Pong frame received" );
102
+ }
103
+ else if (auto close_frame = std::get_if<CloseFrame>(&var))
104
+ {
105
+ // server initiated close
106
+ if (close_frame->has_reason ())
107
+ {
108
+ logger.log <LogLevel::I>(
109
+ " Close frame received: " + string (close_frame->get_reason ())
110
+ );
111
+ }
112
+ else
113
+ logger.log <LogLevel::I>(" Close frame received" );
114
+ break ;
115
+ }
116
+ else if (auto err = std::get_if<WSError>(&var))
117
+ {
118
+ // error occurred - must close connection
119
+ logger.log <LogLevel::E>(" Error: " + err->message );
120
+ WS_TRYV (client.close (err->close_with_code ));
121
+ return {};
122
+ }
123
+ }
124
+
125
+ return {};
126
+ };
127
+
128
+
129
+ int main ()
130
+ {
131
+ auto res = run ();
132
+ if (!res.has_value ())
133
+ {
134
+ std::cerr << " Error: " << res.error ().message << std::endl;
135
+ return 2 ;
136
+ }
137
+ return 0 ;
138
+ };
0 commit comments