-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathecho_binary_text_client_demo.cpp
More file actions
224 lines (188 loc) · 7.27 KB
/
echo_binary_text_client_demo.cpp
File metadata and controls
224 lines (188 loc) · 7.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/**
* @file
*
* @ingroup example_module
*
* @brief TCP connector (client) that sends binary text message to a
* server, receives message back converted to upper case.
*
* @author Thurman Gillespy
*
* Copyright (c) Thurman Gillespy
* 2019-10-21
*
* Updated 2025-04-23 by Cliff Green for library dependency changes
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*
* Sample make file:
g++ -std=c++17 -Wall -Werror \
-I ../include \
-I ../../utility-rack/include/ \
-I ../../utility-rack/third_party/ \
-I ../../asio/asio/include/ \
echo_binary_text_client_demo.cpp -lpthread -o echo_client
*
*/
#include <iostream>
#include <cstdlib> // EXIT_SUCCESS
#include <cstddef> // std::size_t
#include <cstdint> // std::uint16_t, etc
#include <string>
#include <thread>
#include <chrono>
#include <cassert>
#include "utility/cast_ptr_to.hpp"
#include "net_ip/net_ip.hpp"
#include "net_ip/net_entity.hpp"
#include "net_ip_component/worker.hpp"
#include "serialize/extract_append.hpp"
#include "net_ip/io_type_decls.hpp"
using io_context = asio::io_context;
using io_output = chops::net::tcp_io_output;
using io_interface = chops::net::tcp_io_interface;
using const_buf = asio::const_buffer;
using endpoint = asio::ip::tcp::endpoint;
// process command line args (if any)
bool process_args(int argc, char* argv[], bool& print_errors, std::string& ip_address,
std::string& port) {
const std::string HELP_PRM = "-h";
const std::string PRINT_ERRS = "-e";
const std::string USEAGE = \
"useage: ./echo_client [-h | -e] [ip address/hostname] [port]\n"
" -h Print useage\n"
" -e Print error messages\n"
" ip address Default: 127.0.0.1 (LOCAL LOOP)\n"
" port Default: 5002\n"
" change port and use local loop:\n"
" ./echo_client [-e] \"\" port";
int offset = 0;
if (argc > 4 || (argc > 1 && argv[1] == HELP_PRM)) {
std::cout << USEAGE << std::endl;
return EXIT_FAILURE;
}
if (argc > 1 && argv[1] == PRINT_ERRS) {
print_errors = true;
offset = 1;
}
if (argc == 2 + offset) {
ip_address = argv[1 + offset];
} else if (argc == 3 + offset) {
ip_address = argv[1 + offset];
port = argv[2 + offset];
} else if (argc > 3 + offset) {
std::cout << USEAGE << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
constexpr std::size_t HDR_SIZE{2}; // 1st 2 bytes of data is message size
int main(int argc, char* argv[]) {
const std::string PORT = "5002";
const std::string LOCAL_LOOP = "127.0.0.1";
std::string ip_address = LOCAL_LOOP;
std::string port = PORT;
bool hdr_processed = false;
bool print_errors = false;
// io_interface tcp_iof; // use this to send text messages
if (process_args(argc, argv, print_errors, ip_address, port) == EXIT_FAILURE) {
return EXIT_FAILURE;
}
/**** lambda handlers ****/
// message handler
// receive text, display to console
auto msg_hndlr = [] (const_buf buf, io_output io_out, endpoint ep) {
// create string from buf, omit 1st 2 bytes (header)
std::string s (chops::cast_ptr_to<char>(buf.data()) + 2, buf.size() - 2);
std::cout << s << std::endl;
return true;
};
// message frame handler
// 1st call: buffer contains only the header, return message size, toggle flag
// 2nd call: return 0 to indicate no further prodessing, toggle flag
auto msg_frame = [&hdr_processed] (asio::mutable_buffer buf) -> std::size_t {
if (hdr_processed) {
hdr_processed = false;
return 0;
} else {
hdr_processed = true;
// 1st 2 bytes is message size
// endian correct data marshalling
std::uint16_t size = chops::extract_val<std::endian::big, std::uint16_t>
(chops::cast_ptr_to<std::byte>(buf.data()));
return size;
}
};
// io state change handler
auto io_state_chng_hndlr = [&msg_hndlr, &msg_frame]
(io_interface iof, std::size_t n, bool flag) {
if (flag) {
auto e{iof.start_io(HDR_SIZE, msg_hndlr, msg_frame)};
}
};
// error handler
auto err_func = [&print_errors] (io_interface iof, std::error_code err) {
if (print_errors) {
std::string err_text = err.category().name();
err_text += ": " + std::to_string(err.value()) + ", " +
err.message();
std::cerr << err_text << std::endl;
}
};
// work guard - handles @c std::thread and @c asio::io_context management
chops::net::worker wk;
wk.start();
// create @c net_ip instance
chops::net::net_ip echo_client(wk.get_io_context());
// create a @c tcp_connector network entity
chops::net::net_entity net_entity_connect;
net_entity_connect = echo_client.make_tcp_connector(port.c_str(), ip_address.c_str());
assert(net_entity_connect.is_valid());
// start @c network_entity, emplace handlers
auto se{net_entity_connect.start(io_state_chng_hndlr, err_func)};
// begin
std::cout << "chops-net-ip binary text echo demo - client" << std::endl;
std::cout << " IP address:port = " << (ip_address == "" ? LOCAL_LOOP : ip_address);
std::cout << ":" << port << std::endl;
std::cout << " print error messages: " << (print_errors ? "ON" : "OFF") << std::endl;
std::cout << "Enter text to send, or \'quit\' to exit" << std::endl;
// get text to send from user, exit on 'quit'
std::string s;
bool shutdown = false;
while (!shutdown) {
std::getline (std::cin, s); // user input
if (s == "quit") {
shutdown = true;
continue;
}
// REPLACEMMENT FOR THIS FUNCTIONALITY?
// @c tcp.iof is not valid when there is no network connection
// if (!tcp_iof.is_valid()) {
// std::cout << "no connection..." << std::endl;
// continue; // back to top of loop
// }
// buffer to send entered message from user
chops::mutable_shared_buffer buf_out;
// 1st 2 bytes size of message (header) are the string length
std::uint16_t size_val = static_cast<std::uint16_t>(s.size()); // narrowing, hence the static cast
// endian correct data marshalling
std::byte tbuf[HDR_SIZE]; // temp buffer to hold the header
// write those 2 bytes to the temp buffer
std::size_t result = chops::append_val<std::endian::big, std::uint16_t>(tbuf, size_val);
assert(result == HDR_SIZE);
// now append our header and string data to the output buffer
buf_out.append(tbuf, sizeof(tbuf)); // write the header
buf_out.append(s.data(), s.size()); // now add the text data
// send message to server (TCP_acceptor)
// tcp_iof.send(buf_out.data(), buf_out.size());
auto err{net_entity_connect.visit_io_output([&buf_out] (io_output io_out) {
io_out.send(buf_out.data(), buf_out.size());
} // end lambda
)};
} // end while
// cleanup
auto e{net_entity_connect.stop()};
wk.reset();
return EXIT_SUCCESS;
}