1+ /* * @file
2+ *
3+ * @ingroup example_module
4+ *
5+ * @brief Example of TCP send/recieve text string over local loop network
6+ * connection.
7+ *
8+ * @author Thurman Gillespy
9+ *
10+ * Copyright (c) 2019 Thurman Gillespy
11+ * 4/4/19
12+ *
13+ * Distributed under the Boost Software License, Version 1.0.
14+ * (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
15+ *
16+ * Sample make file:
17+ * g++ -std=c++17 -Wall -Werror \
18+ * -I ../include -I ../include/net_ip/ -I ~/Projects/utility-rack/include/ \
19+ * -I ~/Projects/asio/asio/include \
20+ * local_chat_demo.cpp -lpthread
21+ *
22+ * BUGS:
23+ * - leaks memory like a sieve. Under investigation.
24+ *
25+ */
26+
27+ #include < iostream>
28+ #include < cstdlib> // EXIT_SUCCESS
29+ #include < cstddef> // std::size_t
30+ #include < string>
31+ #include < string_view>
32+ #include < chrono>
33+ #include < thread>
34+ #include < algorithm> // std::for_each
35+ #include < cassert>
36+
37+ #include " net_ip/net_ip.hpp"
38+ #include " net_ip/basic_net_entity.hpp"
39+ #include " component/worker.hpp"
40+ #include " queue/wait_queue.hpp"
41+
42+ using io_context = asio::io_context;
43+ using io_interface = chops::net::tcp_io_interface;
44+ using const_buf = asio::const_buffer;
45+ using tcp_io_interface = chops::net::tcp_io_interface;
46+ using endpoint = asio::ip::tcp::endpoint;
47+
48+ /* * How to use @c chops-net-ip for a simple text send/receive network connection
49+ * over a local loop.
50+ *
51+ * 1. Write message handlers for the @c tcp_connector and @c tcp_acceptor.
52+ *
53+ * The @c tcp_connector_message_handler receives text from a @c tcp_acceptor
54+ * (in-bound messages). In this demo, the handler prints the text to stdout,
55+ *
56+ * The @c tcp_acceptor_message_handler receives the (out-bound) message from
57+ * the @c tcp-connector. In this demo, the handler converts the text to
58+ * uppercase, then returns the text over the same connection back to the
59+ * @c tcp_connector.
60+ *
61+ * In this demo, both message handlers return @c false when the user enters
62+ * @c "quit", otherwise @c true. @c "quit" shuts down both handlers and exits
63+ * the program.
64+ *
65+ * 2. Write @ io_state_change handlers for the @c tcp_connector and @c
66+ * @c tcp_acceptor.
67+ *
68+ * The @c tcp_connector @c io_state_change handler calls @c start_io on the
69+ * provided @c chops::net::tcp_io_interface. It then needs to return a copy
70+ * of the @c io_interface to main. The main copy will use the @c io_interface
71+ * to @c send the text message from the @c tcp_connector to the @c tcp_acceptor.
72+ *
73+ * The @c tcp_acceptor @c io_state_change handler only needs to call @c start_io
74+ * on the provided @c io_interface.
75+ *
76+ * 3. Create an instance of the @c chops::net::worker class, which provides
77+ * @c std::thread and @c asio::io_context management. Call @c worker::start
78+ * before the frist call to the @chops::net::ip facilities, then call
79+ * @c worker::stop when finished.
80+ *
81+ * 4. Create an instance of the @c chops::net::ip::net_ip class. The constructor
82+ * needs an @c asio:io_context, which is provided by the @c get_io_context()
83+ * method of the @c chops::net::worker instance.
84+ *
85+ * 5. Call @c ::make_tcp_connector on the @ net_ip instance, which returns a
86+ * copy of a @c tcp_connecvtor_network_entity.
87+ *
88+ * 6. Call @c ::make_tcp_acceptor on the @ net_ip instance, which returns a
89+ * copy of a @c tcp_acceptor_network_entity.
90+ *
91+ * 7. Call @c ::start() on both both @c network_entity. Each @c tcp_connector
92+ * and @c tcp_acceptor @ network_entity takes its' own @c io_state_change
93+ * handler, and each @ io_state_change handler takes its' own @c message_handler.
94+ *
95+ * 8. Call @c ::send() on the @c chops::net::tcp_io_interface instance to send
96+ * a text string over the local loop network connection.
97+ *
98+ * 9. See the example code and the header files for the signatures of the
99+ * handlers.
100+ *
101+ */
102+
103+ const std::string PORT = " 5001" ;
104+
105+ int main () {
106+
107+ /* lambda callbacks */
108+ // message handler for @c tcp_connector @c network_entity
109+ auto msg_hndlr_connect = [] (const_buf buf, io_interface iof, endpoint ep)
110+ {
111+ // receive data from acceptor, display to user
112+ std::string s (static_cast <const char *> (buf.data ()), buf.size ());
113+ std::cout << s; // s aleady has terminating '\n'
114+
115+ // return false if user entered 'quit', otherwise true
116+ return s == " QUIT\n " ? false : true ;
117+ };
118+
119+ // message handler for @c tcp_acceptor @c message_handler
120+ // receive data from connector, send back to acceptor
121+ auto msg_hndlr_accept = [] (const_buf buf, io_interface iof, endpoint ep)
122+ {
123+ // copy buffer contents into string, convert to uppercase
124+ std::string s (static_cast <const char *> (buf.data ()), buf.size ());
125+ auto to_upper = [] (char & c) { c = ::toupper (c); };
126+ std::for_each (s.begin (), s.end (), to_upper);
127+ // send c-string back over network connection
128+ iof.send (s.c_str (), s.size () + 1 );
129+
130+ // return false if user entered 'quit', otherwise true
131+ return s == " QUIT\n " ? false : true ;
132+ };
133+
134+ // io state change handlers
135+ tcp_io_interface tcp_connect_iof; // used to send text data
136+ // handler for @c tcp_connector
137+ auto io_state_chng_connect = [&tcp_connect_iof, msg_hndlr_connect]
138+ (io_interface iof, std::size_t n, bool flag)
139+ {
140+ iof.start_io (" \n " , msg_hndlr_connect);
141+ // return iof to main
142+ tcp_connect_iof = iof;
143+ };
144+
145+ // handler for @c tcp_acceptor
146+ auto io_state_chng_accept = [msg_hndlr_accept]
147+ (io_interface iof, std::size_t n, bool flag)
148+ {
149+ iof.start_io (" \n " , msg_hndlr_accept);
150+ };
151+
152+ // error handler
153+ auto err_func = [] (io_interface iof, std::error_code err)
154+ { std::cerr << " err_func: " << err << std::endl; };
155+
156+ // work guard - handles @c std::thread and @c asio::io_contect management
157+ chops::net::worker wk;
158+ wk.start ();
159+
160+ // create input (acceptor) and output (connector) tcp connections
161+ chops::net::net_ip chat (wk.get_io_context ());
162+ // make @ tcp_acceptor, receive @c tcp_acceptor_network_entity
163+ auto tane = chat.make_tcp_acceptor (PORT, " 127.0.0.1" );
164+ assert (tane.is_valid ());
165+
166+ // make @c tcp_connector, receive @c tcp_connector_network_entity
167+ auto tcne = chat.make_tcp_connector (PORT, " 127.0.0.1" );
168+ assert (tcne.is_valid ());
169+
170+ // start @c tcp_acceptor network entity, emplace handlers
171+ tane.start (io_state_chng_accept, err_func);
172+ // start @c tcp_connector network entity, emplace handlers
173+ tcne.start (io_state_chng_connect, err_func);
174+
175+ // pause to let things settle down
176+ std::this_thread::sleep_for (std::chrono::milliseconds (300 ));
177+
178+ assert (tcp_connect_iof.is_valid ()); // fails without a pause
179+
180+ std::cout << " network demo over local loop" << std::endl;
181+ std::cout << " enter a string at the prompt" << std::endl;
182+ std::cout << " the string will be returned in uppercase" << std::endl;
183+ std::cout << " enter \' quit\' to exit" << std::endl << std::endl;
184+
185+ // get std::string from user
186+ // send as c-string over network connection
187+ std::string s;
188+ while (s != " quit\n " ) {
189+ std::cout << " > " ;
190+ std::getline (std::cin, s);
191+ s += " \n " ; // needed for deliminator
192+ // send c-string from @c tcp_connector to @c tcp_acceptor
193+ tcp_connect_iof.send (s.c_str (), s.size () + 1 );
194+ // pause so returned string is displayed before next prompt
195+ std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
196+ }
197+
198+ wk.stop ();
199+
200+ return EXIT_SUCCESS;
201+ }
0 commit comments