1
+ /* **
2
+ * rrcp_async_tcp_client.cpp
3
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ *
5
+ * Copyright (c) 2003-2024 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
+ * Moderniced from Claus Klein and ChatGPT
11
+ ***/
12
+
1
13
#include < boost/asio.hpp>
2
14
#include < boost/system/error_code.hpp>
3
15
#include < chrono>
7
19
#include < string_view>
8
20
#include < thread>
9
21
10
- constexpr char ESC = 0x1B ;
11
- constexpr char REPLACE_LF = 0x01 ;
12
- constexpr char REPLACE_CR = 0x02 ;
13
- constexpr char REPLACE_ESC = 0x03 ;
22
+ #include " rrcp_helper.hpp"
14
23
15
24
using boost::asio::ip::tcp;
16
25
using namespace std ::chrono_literals;
17
26
18
- auto esc2char (std::string_view data) -> std::string
19
- {
20
- std::string message;
21
- auto len = data.size ();
22
- for (size_t i = 0 ; i < len; ++i)
23
- {
24
- char c = data[i];
25
-
26
- if (c == ' \r ' )
27
- {
28
- return message;
29
- }
30
-
31
- if (c == ESC)
32
- {
33
- if (i == len - 1 )
34
- {
35
- throw std::runtime_error (" esc2char: Error - message ends with escape character!" );
36
- }
37
-
38
- char next = data[++i];
39
- switch (next)
40
- {
41
- case REPLACE_LF:
42
- c = ' \n ' ;
43
- break ;
44
- case REPLACE_CR:
45
- c = ' \r ' ;
46
- break ;
47
- case REPLACE_ESC:
48
- c = ESC;
49
- break ;
50
- default :
51
- throw std::runtime_error (" esc2char: Error - unexpected ESC character!" );
52
- }
53
- }
54
-
55
- message.push_back (c);
56
- }
57
- return message;
58
- }
59
-
60
- auto char2esc (std::string_view data) -> std::string
61
- {
62
- std::string message;
63
- for (char c : data)
64
- {
65
- switch (c)
66
- {
67
- case ' \n ' :
68
- message.push_back (ESC);
69
- message.push_back (REPLACE_LF);
70
- break ;
71
- case ' \r ' :
72
- message.push_back (ESC);
73
- message.push_back (REPLACE_CR);
74
- break ;
75
- case ESC:
76
- message.push_back (ESC);
77
- message.push_back (REPLACE_ESC);
78
- break ;
79
- default :
80
- message.push_back (c);
81
- break ;
82
- }
83
- }
84
- return message;
85
- }
86
-
87
- class client : public std ::enable_shared_from_this< client >
27
+ class rrcp_client : public std ::enable_shared_from_this< rrcp_client >
88
28
{
89
29
public:
90
- client (boost::asio::io_context& io_context) : socket_(io_context), deadline_(io_context), heartbeat_timer_(io_context)
30
+ explicit rrcp_client (boost::asio::io_context& io_context)
31
+ : socket_(io_context), deadline_(io_context), heartbeat_timer_(io_context)
91
32
{
92
33
deadline_.expires_at (boost::asio::steady_timer::time_point::max ());
93
34
}
94
35
95
- void start (tcp::resolver::results_type endpoints)
36
+ void start (const tcp::resolver::results_type& endpoints)
96
37
{
38
+ deadline_.expires_after (3s);
39
+ check_deadline ();
40
+
97
41
boost::asio::async_connect (socket_, endpoints,
98
42
[self = shared_from_this ()](const boost::system ::error_code& ec, const tcp::endpoint&)
99
43
{
100
44
if (!ec)
101
45
{
102
46
std::cout << " Connected to server.\n " ;
103
47
self->read ();
104
- self->start_heartbeat ();
105
- self->check_deadline ();
48
+ self->send_heartbeat ();
106
49
}
107
50
else
108
51
{
109
- std::cerr << " Failed to connect: " << ec.message () << ' \n ' ;
52
+ // FIXME: this aborts! CK
53
+ throw std::runtime_error (" Failed to connect: " + ec.message ());
110
54
}
111
55
});
112
56
}
@@ -116,9 +60,12 @@ class client : public std::enable_shared_from_this< client >
116
60
std::string message;
117
61
while (std::getline (std::cin, message))
118
62
{
119
- if (stopped_) return ;
63
+ if (stopped_)
64
+ {
65
+ return ;
66
+ }
120
67
121
- message = ' \n ' + char2esc (message) + ' \r ' ;
68
+ message = START + char2esc (message) + STOP ;
122
69
boost::asio::async_write (socket_, boost::asio::buffer (message),
123
70
[self = shared_from_this ()](const boost::system ::error_code& ec, std::size_t )
124
71
{
@@ -128,25 +75,27 @@ class client : public std::enable_shared_from_this< client >
128
75
self->stop ();
129
76
}
130
77
});
78
+
131
79
deadline_.expires_after (3s);
132
80
}
133
81
}
134
82
135
83
void read ()
136
84
{
137
- boost::asio::async_read_until (socket_, boost::asio::dynamic_buffer (input_buffer_), ' \r ' ,
85
+ boost::asio::async_read_until (socket_, boost::asio::dynamic_buffer (input_buffer_), STOP ,
138
86
[self = shared_from_this ()](const boost::system ::error_code& ec, std::size_t length)
139
87
{
140
88
if (!ec)
141
89
{
142
- std::string line = esc2char (self->input_buffer_ .substr (0 , length - 1 ));
90
+ std::string const line = esc2char (self->input_buffer_ .substr (1 , length - 1 )); // w/o START, STOP
143
91
self->input_buffer_ .erase (0 , length);
144
- // XXX if (line != "GPing")
92
+
93
+ if (!line.starts_with (" gPing" ))
145
94
{
146
- std::cout << " Server: " << line << ' \n ' ;
95
+ std::cout << line << ' \n ' ;
147
96
}
148
97
self->read ();
149
- self->deadline_ .expires_after (3s );
98
+ self->deadline_ .expires_after (13s );
150
99
}
151
100
else
152
101
{
@@ -156,19 +105,33 @@ class client : public std::enable_shared_from_this< client >
156
105
});
157
106
}
158
107
159
- void start_heartbeat ()
108
+ void stop ()
109
+ {
110
+ std::cerr << " stop called, disconnecting...\n " ;
111
+ stopped_ = true ;
112
+ boost::system ::error_code ec;
113
+ socket_.close (ec);
114
+ heartbeat_timer_.cancel ();
115
+ deadline_.cancel ();
116
+ }
117
+
118
+ private:
119
+ void send_heartbeat ()
160
120
{
161
- if (stopped_) return ;
121
+ if (stopped_)
122
+ {
123
+ return ;
124
+ }
162
125
163
- std::string heartbeat_message{' \n ' + char2esc (" GPing" ) + ' \r ' };
126
+ std::string heartbeat_message{START + char2esc (" M:Utility GPing" ) + STOP };
164
127
std::cerr << " Send heartbeat: " << heartbeat_message << ' \n ' ;
165
128
boost::asio::async_write (socket_, boost::asio::buffer (heartbeat_message),
166
129
[self = shared_from_this ()](const boost::system ::error_code& ec, std::size_t )
167
130
{
168
131
if (!ec)
169
132
{
170
133
self->heartbeat_timer_ .expires_after (10s);
171
- self->heartbeat_timer_ .async_wait ([self](const boost::system ::error_code&) { self->start_heartbeat (); });
134
+ self->heartbeat_timer_ .async_wait ([self](const boost::system ::error_code&) { self->send_heartbeat (); });
172
135
}
173
136
else
174
137
{
@@ -180,7 +143,10 @@ class client : public std::enable_shared_from_this< client >
180
143
181
144
void check_deadline ()
182
145
{
183
- if (stopped_) return ;
146
+ if (stopped_)
147
+ {
148
+ return ;
149
+ }
184
150
185
151
if (deadline_.expiry () <= boost::asio::steady_timer::clock_type::now ())
186
152
{
@@ -192,41 +158,41 @@ class client : public std::enable_shared_from_this< client >
192
158
deadline_.async_wait ([self = shared_from_this ()](const boost::system ::error_code&) { self->check_deadline (); });
193
159
}
194
160
195
- void stop ()
196
- {
197
- std::cerr << " stop called, disconnecting...\n " ;
198
- stopped_ = true ;
199
- boost::system ::error_code ec;
200
- socket_.close (ec);
201
- heartbeat_timer_.cancel ();
202
- deadline_.cancel ();
203
- }
204
-
205
- private:
206
161
tcp::socket socket_;
207
162
boost::asio::steady_timer deadline_, heartbeat_timer_;
208
163
std::string input_buffer_;
209
164
bool stopped_{false };
210
165
};
211
166
212
- int main (int argc, char * argv[])
167
+ auto main (int argc, char * argv[]) -> int
213
168
{
214
169
if (argc != 3 )
215
170
{
216
171
std::cerr << " Usage: " << argv[0 ] << " <host> <port>\n " ;
217
- return 1 ;
172
+ return EXIT_FAILURE ;
218
173
}
219
174
220
- boost::asio::io_context io_context;
221
- tcp::resolver resolver (io_context);
175
+ try
176
+ {
177
+ boost::asio::io_context io_context;
178
+ tcp::resolver resolver (io_context);
179
+
180
+ auto c = std::make_shared< rrcp_client >(io_context);
181
+ c->start (resolver.resolve (argv[1 ], argv[2 ]));
222
182
223
- auto c = std::make_shared< client >(io_context);
224
- c->start (resolver.resolve (argv[1 ], argv[2 ]));
183
+ std::thread io_thread ([&io_context]() { io_context.run (); });
225
184
226
- std::thread io_thread ([&io_context]() { io_context.run (); });
227
- c->write ();
228
- c->stop ();
229
- io_thread.join ();
185
+ std::this_thread::sleep_for (3s); // NOLINT(misc-include-cleaner)
186
+ c->write ();
187
+
188
+ c->stop ();
189
+ io_thread.join ();
190
+ }
191
+ catch (std::exception & e)
192
+ {
193
+ std::cerr << " Exception: " << e.what () << " \n " ;
194
+ return EXIT_FAILURE;
195
+ }
230
196
231
- return 0 ;
197
+ return EXIT_SUCCESS ;
232
198
}
0 commit comments