From d30e2b9d2b88ec89aa416ec4a29c173ae934667f Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Thu, 11 Apr 2019 19:05:09 -0700 Subject: [PATCH 01/17] header update --- example/local_echo_demo.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/example/local_echo_demo.cpp b/example/local_echo_demo.cpp index 733fc35c..d84e8d35 100644 --- a/example/local_echo_demo.cpp +++ b/example/local_echo_demo.cpp @@ -20,9 +20,6 @@ g++ -std=c++17 -Wall -Werror \ -I ~/Projects/asio/asio/include \ -I ~/Projects/boost_1_69_0/ \ local_echo_demo.cpp -lpthread -o local - * - * BUGS: - * - leaks memory like a sieve. Under investigation. * */ From d48231537f86daae79613cb3720830c49f5a249e Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Thu, 11 Apr 2019 23:33:29 -0700 Subject: [PATCH 02/17] simplified single error handler --- example/simple_chat_demo.cpp | 83 +++++++++--------------------------- 1 file changed, 19 insertions(+), 64 deletions(-) diff --git a/example/simple_chat_demo.cpp b/example/simple_chat_demo.cpp index 799b0941..6afb6eb1 100644 --- a/example/simple_chat_demo.cpp +++ b/example/simple_chat_demo.cpp @@ -16,12 +16,10 @@ g++ -std=c++17 -Wall -Werror \ -I ../include \ -I ~/Projects/utility-rack/include/ \ --I ~/Projects/asio-1.12.2/include/ \ +-I ~/Projects/asio/asio/include/ \ -I ~/Projects/boost_1_69_0/ \ -simple_chat_deo.cpp -lpthread -o chat +simple_chat_demo.cpp -lpthread -o chat * - * BUGS: - * - leaks memory like a sieve. Under investigation. * */ @@ -102,8 +100,8 @@ bool process_args(int argc, char* argv[], std::string& ip_addr, const std::string USAGE = "usage: ./chat [-h] -connect | -accept [ip address] [port]\n" " -h print usage\n" - " -connect tcp_acceptor\n" - " -accept tcp_connector\n" + " -connect tcp_connector\n" + " -accept tcp_acceptor\n" " default ip address: " + LOCAL_LOOP + " (local loop)\n" " default port: " + PORT + "\n" " if connection type = accept, IP address becomes \"\""; @@ -164,8 +162,6 @@ int main(int argc, char* argv[]) { std::string ip_addr; std::string port; std::string param; - std::string connect_errs = "tcp_connector errors\n"; - std::string accept_errs = "tcp_acceptor errors\n"; if (process_args(argc, argv, ip_addr, port, param) == EXIT_FAILURE) { return EXIT_FAILURE; @@ -173,9 +169,6 @@ int main(int argc, char* argv[]) { // create instance of @c simple_chat_screen class simple_chat_screen screen(ip_addr, port, param); - // create error log, clear old log - // FILE* file_ptr = fopen(ERR_LOG, "w"); - // assert(file_ptr != nullptr); /* lambda callbacks */ // message handler for @c network_entity @@ -194,7 +187,9 @@ int main(int argc, char* argv[]) { // io state change handler tcp_io_interface tcp_iof; // used to send text data // handler for @c tcp_connector - auto io_state_chng_hndlr = [&tcp_iof, msg_hndlr, DELIM](io_interface iof, std::size_t n, bool flag) { + auto io_state_chng_hndlr = [&tcp_iof, msg_hndlr, DELIM] + (io_interface iof, std::size_t n, bool flag) { + // only start the iof if flag is true (startup) and only 1 if (flag && n == 1) { iof.start_io(DELIM, msg_hndlr); // return iof to main @@ -203,29 +198,12 @@ int main(int argc, char* argv[]) { }; // error handler - auto connect_err_func = [&] (io_interface iof, std::error_code err) + auto err_func = [&] (io_interface iof, std::error_code err) { static int count = 0; - static int last_err = 0; - std::string err_text; - if (err.value() == last_err) { - ++count; - } else { - if (count > 0) { - connect_errs += " <" + std::to_string(count) + ">\n"; - count = 0; - last_err = err.value(); - } - err_text = "connect: " + std::to_string(err.value()) + ": " + - err.category().name() + ": " + err.message() + "\n"; - connect_errs += err_text; - ++count; - } - if (count > 10) { + if (++count > 10) { std::cerr << ABORT << std::endl; - connect_errs += " <" + std::to_string(count) + ">\n"; - std::cerr << connect_errs; exit(0); } @@ -233,33 +211,12 @@ int main(int argc, char* argv[]) { screen.insert_scroll_line(WAIT_CONNECT, SYSTEM); screen.draw_screen(); } - }; - - auto accept_err_func = [&] (io_interface iof, std::error_code err) { - static int count = 0; - static int last_err = 0; - std::string err_text; - if (err.value() == last_err) { - ++count; - } else { - if (count > 0) { - accept_errs += " <" + std::to_string(count) + ">\n"; - count = 0; - last_err = err.value(); - } - err_text = "accept: " + std::to_string(err.value()) + ": " + - err.category().name() + ": " + err.message() + "\n"; - accept_errs += err_text; - ++count; - } - if (count > 10) { - std::cerr << ABORT << std::endl; - accept_errs += " <" + std::to_string(count) + ">\n"; - std::cerr << accept_errs; - exit(0); - } - }; + // uncomment to see the error messages + // std::string err_text = std::to_string(err.value()) + " " + + // err.category().name() + " " + err.message(); + // screen.insert_scroll_line(err_text, SYSTEM); + }; // work guard - handles @c std::thread and @c asio::io_context management chops::net::worker wk; @@ -275,22 +232,21 @@ int main(int argc, char* argv[]) { std::chrono::milliseconds(5000)); assert(net_entity.is_valid()); // start network entity, emplace handlers - net_entity.start(io_state_chng_hndlr, connect_err_func); + net_entity.start(io_state_chng_hndlr, err_func); } else { // make @ tcp_acceptor, return @c network_entity auto net_entity = chat.make_tcp_acceptor(port.c_str(), ip_addr.c_str()); assert(net_entity.is_valid()); // start network entity, emplace handlers - net_entity.start(io_state_chng_hndlr, accept_err_func); + net_entity.start(io_state_chng_hndlr, err_func); } screen.draw_screen(); - // get std::string from user - // send as c-string over network connection, update screen + // get std::string from user, send string data over network, update screen std::string s; while (s != "quit" + DELIM) { - std::getline (std::cin, s); + std::getline (std::cin, s); // user input s += DELIM; // needed for deliminator screen.insert_scroll_line(s, LOCAL); screen.draw_screen(); @@ -299,8 +255,7 @@ int main(int argc, char* argv[]) { screen.draw_screen(); continue; } - // send string data from @c tcp_connector to @c tcp_acceptor - // note correct method of sending std::string over network + // note correct method of sending string data over network tcp_iof.send(s.data(), s.size()); } // allow last message to be sent before shutting down connection From d2d7cf7d870f820c96a73c00696f7761c1c39a93 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Thu, 11 Apr 2019 23:33:53 -0700 Subject: [PATCH 03/17] header updates --- example/local_echo_demo.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/example/local_echo_demo.cpp b/example/local_echo_demo.cpp index d84e8d35..7e011dc6 100644 --- a/example/local_echo_demo.cpp +++ b/example/local_echo_demo.cpp @@ -123,8 +123,8 @@ int main() { std::string s(static_cast (buf.data()), buf.size()); auto to_upper = [] (char& c) { c = ::toupper(c); }; std::for_each(s.begin(), s.end(), to_upper); - // send c-string back over network connection - iof.send(s.c_str(), s.size() + 1); + // send uppercase string data back over network connection + iof.send(s.data(), s.size()); // return false if user entered 'quit', otherwise true return s == "QUIT\n" ? false: true; @@ -183,19 +183,19 @@ int main() { assert(tcp_connect_iof.is_valid()); // fails without a pause - std::cout << "network demo over local loop" << std::endl; + std::cout << "network echo demo over local loop" << std::endl; std::cout << "enter a string at the prompt" << std::endl; std::cout << "the string will be returned in uppercase" << std::endl; std::cout << "enter \'quit\' to exit" << std::endl << std::endl; // get std::string from user - // send as c-string over network connection + // send string data over network connection std::string s; while (s != "quit\n") { std::cout << "> "; std::getline (std::cin, s); s += "\n"; // needed for deliminator - // send c-string from @c tcp_connector to @c tcp_acceptor + // send string from @c tcp_connector to @c tcp_acceptor tcp_connect_iof.send(s.data(), s.size()); // pause so returned string is displayed before next prompt std::this_thread::sleep_for(std::chrono::milliseconds(100)); From 4bf11c0ed2964610c6bcc1a1a5920a5848ae69c8 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Thu, 11 Apr 2019 23:34:39 -0700 Subject: [PATCH 04/17] clear_screen clarification --- example/simple_chat_screen.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/example/simple_chat_screen.hpp b/example/simple_chat_screen.hpp index 52d8b968..7635c95b 100644 --- a/example/simple_chat_screen.hpp +++ b/example/simple_chat_screen.hpp @@ -18,6 +18,8 @@ #include #include #include // std::system +#include +#include const int NUN_SCROLL_LINES = 10; @@ -119,8 +121,15 @@ class simple_chat_screen { } } + // not recommended, but adequate for this demo + // for problems with system("clear") see + // http://www.cplusplus.com/articles/4z18T05o/ void clear_screen() { + #ifdef _WIN32 + system("cls"); + #else system("clear"); + #endif } }; From dac2680d117cf7a2e16229237fa0d02a568330c7 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Fri, 12 Apr 2019 16:43:31 -0700 Subject: [PATCH 05/17] early draft --- example/multichat_server_demo.cpp | 190 ++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 example/multichat_server_demo.cpp diff --git a/example/multichat_server_demo.cpp b/example/multichat_server_demo.cpp new file mode 100644 index 00000000..c3cbd97e --- /dev/null +++ b/example/multichat_server_demo.cpp @@ -0,0 +1,190 @@ +/** @file + * + * @ingroup example_module + * + * @brief Example of TCP multichat server network program. + * + * @author Thurman Gillespy + * + * Copyright (c) 2019 Thurman Gillespy + * 4/12/19 + * + * 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 ~/Projects/utility-rack/include/ \ +-I ~/Projects/asio/asio/include/ \ +-I ~/Projects/boost_1_69_0/ \ +multichat_server_demo.cpp -lpthread -o server + * + */ + +#include +#include // EXIT_SUCCESS +#include // std::size_t +#include +#include +#include +#include + +#include "net_ip/net_ip.hpp" +#include "net_ip/basic_net_entity.hpp" +#include "net_ip/component/worker.hpp" +#include "net_ip/component/send_to_all.hpp" +#include "simple_chat_screen.hpp" + +using io_context = asio::io_context; +using io_interface = chops::net::tcp_io_interface; +using const_buf = asio::const_buffer; +using endpoint = asio::ip::tcp::endpoint; + +// process command line args, set ip_addr, port, param as needed +bool process_args(int argc, char *argv[], std::string &ip_addr, + std::string &port, std::string ¶m) +{ + const std::string PORT = "5001"; + const std::string LOCAL_LOOP = "127.0.0.1"; + const std::string USAGE = + "usage:\n" + " ./mchat [-h] print usage info\n" + " ./mchat -accept [port] multichat server\n" + " ./mchat -connect [IP address/hostname] [port] multichat client\n"; + " default port = " + PORT + "\n"; + const std::string HELP = "-h"; + const std::string EMPTY = ""; + + // set default values + ip_addr = LOCAL_LOOP; + port = PORT; + + std::cout << USAGE << std::endl; + + return EXIT_SUCCESS; +} + +struct io_handler_mock +{ + using socket_type = int; + using endpoint_type = asio::ip::udp::endpoint; + + socket_type sock = 3; + bool started = false; + constexpr static std::size_t qs_base = 42; + + bool is_io_started() const { return started; } + + socket_type &get_socket() { return sock; } + + chops::net::output_queue_stats get_output_queue_stats() const + { + return chops::net::output_queue_stats{qs_base, qs_base + 1}; + } + bool send_called = false; + + void send(chops::const_shared_buffer) { send_called = true; } + void send(chops::const_shared_buffer, const endpoint_type &) { send_called = true; } + + bool mf_sio_called = false; + bool delim_sio_called = false; + bool rd_sio_called = false; + bool rd_endp_sio_called = false; + bool send_sio_called = false; + bool send_endp_sio_called = false; + + template + bool start_io(std::size_t, MH &&, MF &&) + { + return started ? false : started = true, mf_sio_called = true, true; + } + + template + bool start_io(std::string_view, MH &&) + { + return started ? false : started = true, delim_sio_called = true, true; + } + + template + bool start_io(std::size_t, MH &&) + { + return started ? false : started = true, rd_sio_called = true, true; + } + + template + bool start_io(const endpoint_type &, std::size_t, MH &&) + { + return started ? false : started = true, rd_endp_sio_called = true, true; + } + + bool start_io() + { + return started ? false : started = true, send_sio_called = true, true; + } + + bool start_io(const endpoint_type &) + { + return started ? false : started = true, send_endp_sio_called = true, true; + } + + bool stop_io() + { + return started ? started = false, true : false; + } +}; + +int main(int argc, char *argv[]) +{ + const std::string LOCAL = "[local] "; + const std::string SYSTEM = "[system] "; + const std::string SERVER = "[server] "; + const std::string DELIM = "\a"; // alert (bell) + const std::string NO_CONNECTION = "error: no connection" + DELIM; + const std::string ABORT = "abort: too many errors"; + const std::string WAIT_CONNECT = "waiting for connection..." + DELIM; + std::string ip_addr; + std::string port; + std::string param; + + // work guard - handles @c std::thread and @c asio::io_context management + chops::net::worker wk; + wk.start(); + + chops::net::send_to_all sta; + + if (process_args(argc, argv, ip_addr, port, param) == EXIT_FAILURE) + { + return EXIT_FAILURE; + } + + /* lamda handlers */ + // receive text from client, send out to others + auto msg_hndlr = [&](const_buf buf, io_interface iof, endpoint ep) { + + }; + + auto io_state_chng_hndlr = [&](io_interface iof, std::size_t n, bool flag) { + // call start_io, add to or remove from list + iof.start_io(DELIM, msg_hndlr); + sta.add_io_interface(iof); + }; + + auto err_func = [&](io_interface iof, std::error_code err) { + + }; + + + // create @c net_ip instance + chops::net::net_ip server(wk.get_io_context()); + // make @c tcp_acceptor, ruitn @c network_entity + auto net_entity = server.make_tcp_acceptor(port.c_str(), ip_addr.c_str()); + // start network entity, emplace handlers + net_entity.start(io_state_chng_hndlr, err_func); + + while (!finished) { + + } + + return EXIT_SUCCESS; +} \ No newline at end of file From ffbdb6e72689eaa983e3880b0559413d669e934f Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Sat, 13 Apr 2019 13:13:05 -0700 Subject: [PATCH 06/17] extended send() interface; send to all except io_intf passed as 3rd param --- include/net_ip/component/send_to_all.hpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/net_ip/component/send_to_all.hpp b/include/net_ip/component/send_to_all.hpp index 3dd1a80d..5713b29a 100644 --- a/include/net_ip/component/send_to_all.hpp +++ b/include/net_ip/component/send_to_all.hpp @@ -102,6 +102,16 @@ class send_to_all { io.send(buf); } } + + void send(chops::const_shared_buffer buf, io_intf cur_io) const { // TG + lock_guard gd { m_mutex }; + for (const auto& io : m_io_intfs) { + if ( !(cur_io == io) ) { + io.send(buf); + } + } + } + /** * @brief Copy the bytes, create a reference counted buffer, then send it to * all @c basic_io_interface objects. @@ -109,6 +119,11 @@ class send_to_all { void send(const void* buf, std::size_t sz) const { send(chops::const_shared_buffer(buf, sz)); } + + void send(const void* buf, std::size_t sz, io_intf cur_io) const { // TG + send(chops::const_shared_buffer(buf, sz), cur_io); + } + /** * @brief Move the buffer from a writable reference counted buffer to a * immutable reference counted buffer, then send it. @@ -116,6 +131,11 @@ class send_to_all { void send(chops::mutable_shared_buffer&& buf) const { send(chops::const_shared_buffer(std::move(buf))); } + + void send(chops::mutable_shared_buffer&& buf, io_intf cur_io) const { + send(chops::const_shared_buffer(std::move(buf)), cur_io); + } + /** * @brief Return the number of @c basic_io_interface objects in the collection. */ From a91408ba8b89d913648553a9d501ba0a1c6fc007 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Sat, 13 Apr 2019 13:13:57 -0700 Subject: [PATCH 07/17] first working version, with extended version of send_to_all::send --- example/multichat_server_demo.cpp | 228 ++++++++++++------------------ 1 file changed, 88 insertions(+), 140 deletions(-) diff --git a/example/multichat_server_demo.cpp b/example/multichat_server_demo.cpp index c3cbd97e..747eab17 100644 --- a/example/multichat_server_demo.cpp +++ b/example/multichat_server_demo.cpp @@ -28,6 +28,7 @@ multichat_server_demo.cpp -lpthread -o server #include #include #include +#include // std::ref #include #include "net_ip/net_ip.hpp" @@ -43,148 +44,95 @@ using endpoint = asio::ip::tcp::endpoint; // process command line args, set ip_addr, port, param as needed bool process_args(int argc, char *argv[], std::string &ip_addr, - std::string &port, std::string ¶m) -{ - const std::string PORT = "5001"; - const std::string LOCAL_LOOP = "127.0.0.1"; - const std::string USAGE = - "usage:\n" - " ./mchat [-h] print usage info\n" - " ./mchat -accept [port] multichat server\n" - " ./mchat -connect [IP address/hostname] [port] multichat client\n"; - " default port = " + PORT + "\n"; - const std::string HELP = "-h"; - const std::string EMPTY = ""; - - // set default values - ip_addr = LOCAL_LOOP; - port = PORT; - - std::cout << USAGE << std::endl; - - return EXIT_SUCCESS; + std::string &port, std::string ¶m) { + const std::string PORT = "5001"; + const std::string LOCAL_LOOP = "127.0.0.1"; + const std::string USAGE = + "usage:\n" + " ./chat_server [-h] [port]\n" + " -h print usage\n" + " default port = " + PORT + "\n" + " server IP address = " + LOCAL_LOOP + " (local loop)"; + + const std::string HELP = "-h"; + + // set default values + ip_addr = LOCAL_LOOP; + port = PORT; + + if (argc > 2 || (argc == 2 && argv[1] == HELP)) { + std::cout << USAGE << std::endl; + return EXIT_FAILURE; + } + + if (argc == 2) { + port = argv[1]; + } + + return EXIT_SUCCESS; } -struct io_handler_mock -{ - using socket_type = int; - using endpoint_type = asio::ip::udp::endpoint; - - socket_type sock = 3; - bool started = false; - constexpr static std::size_t qs_base = 42; - - bool is_io_started() const { return started; } - - socket_type &get_socket() { return sock; } - - chops::net::output_queue_stats get_output_queue_stats() const - { - return chops::net::output_queue_stats{qs_base, qs_base + 1}; - } - bool send_called = false; - - void send(chops::const_shared_buffer) { send_called = true; } - void send(chops::const_shared_buffer, const endpoint_type &) { send_called = true; } - - bool mf_sio_called = false; - bool delim_sio_called = false; - bool rd_sio_called = false; - bool rd_endp_sio_called = false; - bool send_sio_called = false; - bool send_endp_sio_called = false; - - template - bool start_io(std::size_t, MH &&, MF &&) - { - return started ? false : started = true, mf_sio_called = true, true; - } - - template - bool start_io(std::string_view, MH &&) - { - return started ? false : started = true, delim_sio_called = true, true; - } - - template - bool start_io(std::size_t, MH &&) - { - return started ? false : started = true, rd_sio_called = true, true; - } - - template - bool start_io(const endpoint_type &, std::size_t, MH &&) - { - return started ? false : started = true, rd_endp_sio_called = true, true; - } - - bool start_io() - { - return started ? false : started = true, send_sio_called = true, true; - } - - bool start_io(const endpoint_type &) - { - return started ? false : started = true, send_endp_sio_called = true, true; - } - - bool stop_io() - { - return started ? started = false, true : false; - } -}; - int main(int argc, char *argv[]) { - const std::string LOCAL = "[local] "; - const std::string SYSTEM = "[system] "; - const std::string SERVER = "[server] "; - const std::string DELIM = "\a"; // alert (bell) - const std::string NO_CONNECTION = "error: no connection" + DELIM; - const std::string ABORT = "abort: too many errors"; - const std::string WAIT_CONNECT = "waiting for connection..." + DELIM; - std::string ip_addr; - std::string port; - std::string param; - - // work guard - handles @c std::thread and @c asio::io_context management - chops::net::worker wk; - wk.start(); - - chops::net::send_to_all sta; - - if (process_args(argc, argv, ip_addr, port, param) == EXIT_FAILURE) - { - return EXIT_FAILURE; - } - - /* lamda handlers */ - // receive text from client, send out to others - auto msg_hndlr = [&](const_buf buf, io_interface iof, endpoint ep) { - - }; - - auto io_state_chng_hndlr = [&](io_interface iof, std::size_t n, bool flag) { - // call start_io, add to or remove from list - iof.start_io(DELIM, msg_hndlr); - sta.add_io_interface(iof); - }; - - auto err_func = [&](io_interface iof, std::error_code err) { - - }; - - - // create @c net_ip instance - chops::net::net_ip server(wk.get_io_context()); - // make @c tcp_acceptor, ruitn @c network_entity - auto net_entity = server.make_tcp_acceptor(port.c_str(), ip_addr.c_str()); - // start network entity, emplace handlers - net_entity.start(io_state_chng_hndlr, err_func); - - while (!finished) { - - } - - return EXIT_SUCCESS; + const std::string LOCAL = "[local] "; + const std::string SYSTEM = "[system] "; + const std::string SERVER = "[server] "; + const std::string DELIM = "\a"; // alert (bell) + const std::string NO_CONNECTION = "error: no connection" + DELIM; + const std::string ABORT = "abort: too many errors"; + const std::string WAIT_CONNECT = "waiting for connection..." + DELIM; + std::string ip_addr; + std::string port; + std::string param; + bool finished = false; + + if (process_args(argc, argv, ip_addr, port, param) == EXIT_FAILURE) { + return EXIT_FAILURE; + } + // DEBUG + // std::cout << ip_addr << ":" << port << std::endl; + // return 0; + + // work guard - handles @c std::thread and @c asio::io_context management + chops::net::worker wk; + wk.start(); + + chops::net::send_to_all sta; + /* lamda handlers */ + // receive text from client, send out to others + auto msg_hndlr = [&](const_buf buf, io_interface iof, endpoint ep) { + sta.send(buf.data(), buf.size(), iof); + return true; + }; + + auto io_state_chng_hndlr = [&](io_interface iof, std::size_t n, bool flag) { + // add to or remove from list + sta(iof, n, flag); + if (flag) { + iof.start_io(DELIM, msg_hndlr); + } + }; + + auto err_func = [&](io_interface iof, std::error_code err) { + std::cerr << err << " " << err.message() << std::endl; + }; + + // create @c net_ip instance + chops::net::net_ip server(wk.get_io_context()); + // make @c tcp_acceptor, ruitn @c network_entity + auto net_entity = server.make_tcp_acceptor(port.c_str(), ip_addr.c_str()); + // start network entity, emplace handlers + net_entity.start(io_state_chng_hndlr, err_func); + + while (!finished) { + std::string s; + std::cout << "press any key to exit" << std::endl; + getline(std::cin, s); + finished = true; + } + + wk.stop(); + + + return EXIT_SUCCESS; } \ No newline at end of file From 93d9428317fdba04bbed22082e2c7926b55d4667 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Sat, 13 Apr 2019 22:44:12 -0700 Subject: [PATCH 08/17] send message to clients when server shutting down --- example/multichat_server_demo.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/example/multichat_server_demo.cpp b/example/multichat_server_demo.cpp index 747eab17..c616f02e 100644 --- a/example/multichat_server_demo.cpp +++ b/example/multichat_server_demo.cpp @@ -89,15 +89,14 @@ int main(int argc, char *argv[]) if (process_args(argc, argv, ip_addr, port, param) == EXIT_FAILURE) { return EXIT_FAILURE; } - // DEBUG - // std::cout << ip_addr << ":" << port << std::endl; - // return 0; // work guard - handles @c std::thread and @c asio::io_context management chops::net::worker wk; wk.start(); + // handles all @c io_interfaces chops::net::send_to_all sta; + /* lamda handlers */ // receive text from client, send out to others auto msg_hndlr = [&](const_buf buf, io_interface iof, endpoint ep) { @@ -124,13 +123,19 @@ int main(int argc, char *argv[]) // start network entity, emplace handlers net_entity.start(io_state_chng_hndlr, err_func); + std::cout << "chops-net-ip chat sever demo" << std::endl; + while (!finished) { std::string s; std::cout << "press any key to exit" << std::endl; getline(std::cin, s); + s = "server shutting down" + DELIM; + sta.send(s.data(), s.size()); finished = true; } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + wk.stop(); From 2f95feab2febf1574726b8a1a8ec280fa9921e5f Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Sun, 14 Apr 2019 07:15:36 -0700 Subject: [PATCH 09/17] moved work guard before instatiatiing send_to_all --- example/multichat_server_demo.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/example/multichat_server_demo.cpp b/example/multichat_server_demo.cpp index c616f02e..bd2d4912 100644 --- a/example/multichat_server_demo.cpp +++ b/example/multichat_server_demo.cpp @@ -99,13 +99,15 @@ int main(int argc, char *argv[]) /* lamda handlers */ // receive text from client, send out to others - auto msg_hndlr = [&](const_buf buf, io_interface iof, endpoint ep) { + auto msg_hndlr = [&sta](const_buf buf, io_interface iof, endpoint ep) { sta.send(buf.data(), buf.size(), iof); + return true; }; - auto io_state_chng_hndlr = [&](io_interface iof, std::size_t n, bool flag) { - // add to or remove from list + auto io_state_chng_hndlr = [&sta, &msg_hndlr, &DELIM] + (io_interface iof, std::size_t n, bool flag) { + // add to or remove @c io_interface from list in sta sta(iof, n, flag); if (flag) { iof.start_io(DELIM, msg_hndlr); @@ -119,7 +121,8 @@ int main(int argc, char *argv[]) // create @c net_ip instance chops::net::net_ip server(wk.get_io_context()); // make @c tcp_acceptor, ruitn @c network_entity - auto net_entity = server.make_tcp_acceptor(port.c_str(), ip_addr.c_str()); + auto net_entity = server.make_tcp_acceptor(port.c_str()); + assert(net_entity.is_valid()); // start network entity, emplace handlers net_entity.start(io_state_chng_hndlr, err_func); @@ -127,14 +130,15 @@ int main(int argc, char *argv[]) while (!finished) { std::string s; - std::cout << "press any key to exit" << std::endl; + std::cout << "press return to exit" << std::endl; getline(std::cin, s); s = "server shutting down" + DELIM; sta.send(s.data(), s.size()); finished = true; } - + // delay so message gets sent std::this_thread::sleep_for(std::chrono::milliseconds(500)); + // shutdown code here? wk.stop(); From e7454ddf4d13898886acea85b41b70a7e0a28a31 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Sun, 14 Apr 2019 23:17:40 -0700 Subject: [PATCH 10/17] error log info in header --- example/simple_chat_screen.hpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/example/simple_chat_screen.hpp b/example/simple_chat_screen.hpp index 7635c95b..fdafd726 100644 --- a/example/simple_chat_screen.hpp +++ b/example/simple_chat_screen.hpp @@ -5,7 +5,7 @@ * @author Thurman Gillespy * * Copyright (c) 2019 Thurman Gillespy - * 4/11/19 + * 4/14/19 * * 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) @@ -18,8 +18,6 @@ #include #include #include // std::system -#include -#include const int NUN_SCROLL_LINES = 10; @@ -36,13 +34,13 @@ class simple_chat_screen { const std::string m_connect_type; // @c '-connect' or @c '-accept' std::string m_upper_screen; // fixed upper region of screen output std::string m_scroll_text; // history scroll text region - const int m_num_scroll_lines; // number of 'scrol lines' + const int m_num_scroll_lines; // number of 'scroll lines' public: - simple_chat_screen(const std::string ip, const std::string port, const std::string type, - int num_lines = NUN_SCROLL_LINES) : + simple_chat_screen(const std::string& ip, const std::string& port, const std::string& type, + bool print_errors, int num_lines = NUN_SCROLL_LINES) : m_ip_addr(ip), m_port(port), m_connect_type(type), m_num_scroll_lines(num_lines) { - create_upper_screen(); + create_upper_screen(print_errors); create_scroll_text(); }; @@ -81,7 +79,7 @@ class simple_chat_screen { S DIVIDOR = "|___________________________________________________________________________|\n"; S HDR_1 = - "| chops-net-ip 2-way chat network demo |\n"; + "| chops-net-ip chat network demo |\n"; S HDR_IP = "| IP address: "; S HDR_PORT = @@ -92,6 +90,10 @@ class simple_chat_screen { "connector |\n"; S ACCEPT_T = "acceptor |\n"; + S ERR_LOG_ON = + "| errors printed to console: ON |\n"; + S ERR_LOG_OFF = + "| errors printed to console: OFF |\n"; S HDR_INSTR = "| Enter text to send at prompt. Enter 'quit' to exit. |\n"; S SCROLL_LINE = @@ -103,13 +105,14 @@ class simple_chat_screen { S PROMPT = "> "; // create the string that represent the (unchanging) upper screen so only calculate once - void create_upper_screen() { + void create_upper_screen(bool print_err) { std::string hdr_info = HDR_IP + (m_ip_addr == "" ? "\"\"" : m_ip_addr) + HDR_PORT + m_port ; hdr_info += BLANK_LINE.substr(hdr_info.size(), std::string::npos); m_upper_screen = TOP + BLANK_LINE + HDR_1 + DIVIDOR + BLANK_LINE + hdr_info + HDR_TYPE + - (m_connect_type == PARAM_ACCEPT ? ACCEPT_T : CONNECT_T) + + (m_connect_type == PARAM_ACCEPT ? ACCEPT_T : CONNECT_T) + + (print_err ? ERR_LOG_ON : ERR_LOG_OFF) + DIVIDOR + BLANK_LINE + HDR_INSTR + DIVIDOR; } From ac88875ff3412d45d9a337a320799c944bbc72fd Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Sun, 14 Apr 2019 23:18:39 -0700 Subject: [PATCH 11/17] header & ijcludes cleanjp; added -e option (print errors) --- example/simple_chat_demo.cpp | 94 +++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 27 deletions(-) diff --git a/example/simple_chat_demo.cpp b/example/simple_chat_demo.cpp index 6afb6eb1..becf153f 100644 --- a/example/simple_chat_demo.cpp +++ b/example/simple_chat_demo.cpp @@ -29,7 +29,6 @@ simple_chat_demo.cpp -lpthread -o chat #include #include #include -#include #include #include "net_ip/net_ip.hpp" @@ -93,26 +92,28 @@ using endpoint = asio::ip::tcp::endpoint; // process command line args, set ip_addr, port, param as needed -bool process_args(int argc, char* argv[], std::string& ip_addr, - std::string& port, std::string& param) { +bool process_args(int argc, char* const argv[], std::string& ip_addr, + std::string& port, std::string& param, bool& print_errors) { const std::string PORT = "5001"; const std::string LOCAL_LOOP = "127.0.0.1"; const std::string USAGE = - "usage: ./chat [-h] -connect | -accept [ip address] [port]\n" + "usage: ./chat [-h] [-e] -connect | -accept [ip address] [port]\n" " -h print usage\n" + " -e print error messages\n" " -connect tcp_connector\n" " -accept tcp_acceptor\n" " default ip address: " + LOCAL_LOOP + " (local loop)\n" " default port: " + PORT + "\n" " if connection type = accept, IP address becomes \"\""; const std::string HELP = "-h"; + const std::string ERR = "-e"; const std::string EMPTY = ""; // set default values ip_addr = LOCAL_LOOP; port = PORT; - if (argc < 2 || argc > 4) { + if (argc < 2 || argc > 5) { std::cout << "incorrect parameter count\n"; std::cout << USAGE << std::endl; return EXIT_FAILURE; @@ -121,29 +122,62 @@ bool process_args(int argc, char* argv[], std::string& ip_addr, if (argv[1] == HELP) { std::cout << USAGE << std::endl; return EXIT_FAILURE; - } else if (argv[1] == PARAM_CONNECT) { - param = PARAM_CONNECT; - } else if (argv[1] == PARAM_ACCEPT) { - param = PARAM_ACCEPT; + } else if (argv[1] == ERR) { + print_errors = true; + } + std::cerr << (print_errors ? "true" : "false") << std::endl; + if (print_errors) { + if (argc == 2) { + std::cout << USAGE << std::endl; + return EXIT_FAILURE; + } + if (argv[2] == PARAM_CONNECT) { + param = PARAM_CONNECT; + } else if (argv[2] == PARAM_ACCEPT) { + param = PARAM_ACCEPT; + } else { + std::cout << "incorrect second parameter: "; + std::cout << "must be [-connect | -accept]" << std::endl; + std::cout << USAGE << std::endl; + return EXIT_FAILURE; + } } else { - std::cout << "incorrect first parameter: "; - std::cout << "must be [-h | -connect | -accept]" << std::endl; - std::cout << USAGE << std::endl; - return EXIT_FAILURE; + if (argv[1] == PARAM_CONNECT) { + param = PARAM_CONNECT; + } else if (argv[1] == PARAM_ACCEPT) { + param = PARAM_ACCEPT; + } else { + std::cout << "incorrect first parameter: "; + std::cout << "must be [-h | -e | -connect | -accept]" << std::endl; + std::cout << USAGE << std::endl; + return EXIT_FAILURE; + } } if (param == PARAM_ACCEPT) { ip_addr = ""; } - if (argc == 3 || argc == 4) { - if (param == PARAM_CONNECT) { - ip_addr = argv[2]; + if (print_errors) { + if (argc == 4 || argc == 5) { + if (param == PARAM_CONNECT) { + ip_addr = argv[3]; + } } - } - if (argc == 4) { - port = argv[3]; + if (argc == 5) { + port = argv[4]; + } + } else { + if (argc == 3 || argc == 4) { + if (param == PARAM_CONNECT) { + ip_addr = argv[2]; + } + } + + if (argc == 4) { + port = argv[3]; + } } assert(param != ""); @@ -154,21 +188,22 @@ bool process_args(int argc, char* argv[], std::string& ip_addr, int main(int argc, char* argv[]) { const std::string LOCAL = "[local] "; const std::string SYSTEM = "[system] "; + const std::string ERROR = "[error] "; const std::string DELIM = "\a"; // alert (bell) const std::string NO_CONNECTION = "error: no connection" + DELIM; const std::string ABORT = "abort: too many errors"; const std::string WAIT_CONNECT = "waiting for connection..." + DELIM; - // const char* ERR_LOG = "err_log.txt"; std::string ip_addr; std::string port; std::string param; + bool print_errors = false; - if (process_args(argc, argv, ip_addr, port, param) == EXIT_FAILURE) { + if (process_args(argc, argv, ip_addr, port, param, print_errors) == EXIT_FAILURE) { return EXIT_FAILURE; } // create instance of @c simple_chat_screen class - simple_chat_screen screen(ip_addr, port, param); + simple_chat_screen screen(ip_addr, port, param, print_errors); /* lambda callbacks */ // message handler for @c network_entity @@ -202,7 +237,7 @@ int main(int argc, char* argv[]) { { static int count = 0; - if (++count > 10) { + if (++count > 15) { std::cerr << ABORT << std::endl; exit(0); } @@ -212,10 +247,13 @@ int main(int argc, char* argv[]) { screen.draw_screen(); } - // uncomment to see the error messages - // std::string err_text = std::to_string(err.value()) + " " + - // err.category().name() + " " + err.message(); - // screen.insert_scroll_line(err_text, SYSTEM); + if (print_errors) { + std::string err_text = err.category().name(); + err_text += ": " + std::to_string(err.value()) + ", " + + err.message() + DELIM; + screen.insert_scroll_line(err_text, ERROR); + screen.draw_screen(); + } }; // work guard - handles @c std::thread and @c asio::io_context management @@ -261,6 +299,8 @@ int main(int argc, char* argv[]) { // allow last message to be sent before shutting down connection std::this_thread::sleep_for(std::chrono::seconds(1)); + // tcp_iof.stop_io(); + wk.stop(); return EXIT_SUCCESS; From 56637912290051b78be483752990620aa12b2ee8 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Sun, 14 Apr 2019 23:19:27 -0700 Subject: [PATCH 12/17] added -e option (print errors to console) --- ...t_server_demo.cpp => chat_server_demo.cpp} | 63 ++++++++++++------- 1 file changed, 41 insertions(+), 22 deletions(-) rename example/{multichat_server_demo.cpp => chat_server_demo.cpp} (66%) diff --git a/example/multichat_server_demo.cpp b/example/chat_server_demo.cpp similarity index 66% rename from example/multichat_server_demo.cpp rename to example/chat_server_demo.cpp index bd2d4912..0a1cf51e 100644 --- a/example/multichat_server_demo.cpp +++ b/example/chat_server_demo.cpp @@ -7,7 +7,7 @@ * @author Thurman Gillespy * * Copyright (c) 2019 Thurman Gillespy - * 4/12/19 + * 4/14/19 * * 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) @@ -18,7 +18,7 @@ g++ -std=c++17 -Wall -Werror \ -I ~/Projects/utility-rack/include/ \ -I ~/Projects/asio/asio/include/ \ -I ~/Projects/boost_1_69_0/ \ -multichat_server_demo.cpp -lpthread -o server +chat_server_demo.cpp -lpthread -o chat_server * */ @@ -35,7 +35,6 @@ multichat_server_demo.cpp -lpthread -o server #include "net_ip/basic_net_entity.hpp" #include "net_ip/component/worker.hpp" #include "net_ip/component/send_to_all.hpp" -#include "simple_chat_screen.hpp" using io_context = asio::io_context; using io_interface = chops::net::tcp_io_interface; @@ -43,30 +42,46 @@ using const_buf = asio::const_buffer; using endpoint = asio::ip::tcp::endpoint; // process command line args, set ip_addr, port, param as needed -bool process_args(int argc, char *argv[], std::string &ip_addr, - std::string &port, std::string ¶m) { +bool process_args(int argc, char* const argv[], std::string& ip_addr, + std::string& port, bool& print_errors) { const std::string PORT = "5001"; const std::string LOCAL_LOOP = "127.0.0.1"; const std::string USAGE = "usage:\n" - " ./chat_server [-h] [port]\n" - " -h print usage\n" + " ./chat_server [-h] [-e] [port]\n" + " -h print usage\n" + " -e print all error messages to console\n" " default port = " + PORT + "\n" - " server IP address = " + LOCAL_LOOP + " (local loop)"; + " server IP address (fixed) = " + LOCAL_LOOP + " (local loop)"; const std::string HELP = "-h"; + const std::string ERR = "-e"; // set default values ip_addr = LOCAL_LOOP; port = PORT; - if (argc > 2 || (argc == 2 && argv[1] == HELP)) { + if (argc > 3 || (argc == 2 && argv[1] == HELP)) { std::cout << USAGE << std::endl; return EXIT_FAILURE; } if (argc == 2) { - port = argv[1]; + if (argv[1] == ERR) { + print_errors = true; + } else { + port = argv[1]; + } + } + + if (argc == 3) { + if (argv[1] == ERR) { + print_errors = true; + port = argv[2]; + } else { + std::cout << USAGE << std::endl; + return EXIT_FAILURE; + } } return EXIT_SUCCESS; @@ -78,15 +93,12 @@ int main(int argc, char *argv[]) const std::string SYSTEM = "[system] "; const std::string SERVER = "[server] "; const std::string DELIM = "\a"; // alert (bell) - const std::string NO_CONNECTION = "error: no connection" + DELIM; - const std::string ABORT = "abort: too many errors"; - const std::string WAIT_CONNECT = "waiting for connection..." + DELIM; std::string ip_addr; std::string port; - std::string param; + bool print_errors = false; bool finished = false; - if (process_args(argc, argv, ip_addr, port, param) == EXIT_FAILURE) { + if (process_args(argc, argv, ip_addr, port, print_errors) == EXIT_FAILURE) { return EXIT_FAILURE; } @@ -99,13 +111,13 @@ int main(int argc, char *argv[]) /* lamda handlers */ // receive text from client, send out to others - auto msg_hndlr = [&sta](const_buf buf, io_interface iof, endpoint ep) { + const auto msg_hndlr = [&sta](const_buf buf, io_interface iof, endpoint ep) { sta.send(buf.data(), buf.size(), iof); return true; }; - auto io_state_chng_hndlr = [&sta, &msg_hndlr, &DELIM] + const auto io_state_chng_hndlr = [&sta, &msg_hndlr, &DELIM] (io_interface iof, std::size_t n, bool flag) { // add to or remove @c io_interface from list in sta sta(iof, n, flag); @@ -114,8 +126,10 @@ int main(int argc, char *argv[]) } }; - auto err_func = [&](io_interface iof, std::error_code err) { - std::cerr << err << " " << err.message() << std::endl; + const auto err_func = [&print_errors](io_interface iof, std::error_code err) { + if (print_errors) { + std::cerr << err << ", " << err.message() << std::endl; + } }; // create @c net_ip instance @@ -126,7 +140,11 @@ int main(int argc, char *argv[]) // start network entity, emplace handlers net_entity.start(io_state_chng_hndlr, err_func); - std::cout << "chops-net-ip chat sever demo" << std::endl; + std::cout << "chops-net-ip chat server demo" << std::endl; + std::cout << " port: " << port << std::endl; + if (print_errors) { + std::cout << " all error messages printed to console" << std::endl; + } while (!finished) { std::string s; @@ -137,9 +155,10 @@ int main(int argc, char *argv[]) finished = true; } // delay so message gets sent - std::this_thread::sleep_for(std::chrono::milliseconds(500)); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // shutdown code here? - + // net_entity.stop(); + wk.stop(); From 9da568c3ff26756841779296335080b87c7a91e9 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Mon, 15 Apr 2019 10:36:53 -0700 Subject: [PATCH 13/17] added std::promise/future construct to io_state_chng_hndlr; minor edits --- example/simple_chat_demo.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/example/simple_chat_demo.cpp b/example/simple_chat_demo.cpp index becf153f..a8b23760 100644 --- a/example/simple_chat_demo.cpp +++ b/example/simple_chat_demo.cpp @@ -7,7 +7,7 @@ * @author Thurman Gillespy * * Copyright (c) 2019 Thurman Gillespy - * 4/11/19 + * 4/15/19 * * 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) @@ -29,6 +29,7 @@ simple_chat_demo.cpp -lpthread -o chat #include #include #include +#include // std::promise, std::future #include #include "net_ip/net_ip.hpp" @@ -98,7 +99,7 @@ bool process_args(int argc, char* const argv[], std::string& ip_addr, const std::string LOCAL_LOOP = "127.0.0.1"; const std::string USAGE = "usage: ./chat [-h] [-e] -connect | -accept [ip address] [port]\n" - " -h print usage\n" + " -h print usage info\n" " -e print error messages\n" " -connect tcp_connector\n" " -accept tcp_acceptor\n" @@ -125,7 +126,7 @@ bool process_args(int argc, char* const argv[], std::string& ip_addr, } else if (argv[1] == ERR) { print_errors = true; } - std::cerr << (print_errors ? "true" : "false") << std::endl; + if (print_errors) { if (argc == 2) { std::cout << USAGE << std::endl; @@ -197,6 +198,8 @@ int main(int argc, char* argv[]) { std::string port; std::string param; bool print_errors = false; + std::promise promise_obj; + std::future future_obj = promise_obj.get_future(); if (process_args(argc, argv, ip_addr, port, param, print_errors) == EXIT_FAILURE) { return EXIT_FAILURE; @@ -219,16 +222,17 @@ int main(int argc, char* argv[]) { return s == "quit" + DELIM ? false : true; }; - // io state change handler - tcp_io_interface tcp_iof; // used to send text data // handler for @c tcp_connector - auto io_state_chng_hndlr = [&tcp_iof, msg_hndlr, DELIM] + auto io_state_chng_hndlr = [&promise_obj, msg_hndlr, DELIM] (io_interface iof, std::size_t n, bool flag) { // only start the iof if flag is true (startup) and only 1 if (flag && n == 1) { iof.start_io(DELIM, msg_hndlr); // return iof to main - tcp_iof = iof; + // tcp_iof = iof; + + // return iof via promise/future handoff + promise_obj.set_value(iof); } }; @@ -281,6 +285,9 @@ int main(int argc, char* argv[]) { screen.draw_screen(); + // io state change handler + tcp_io_interface tcp_iof; // used to send text data + tcp_iof = future_obj.get(); // wait for value set in io_state_chng_hndlr // get std::string from user, send string data over network, update screen std::string s; while (s != "quit" + DELIM) { From 6f931724f8c3d4047c095a127588541c3094fcff Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Mon, 15 Apr 2019 10:37:24 -0700 Subject: [PATCH 14/17] added space to PROMPT --- example/simple_chat_screen.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/simple_chat_screen.hpp b/example/simple_chat_screen.hpp index fdafd726..0ccb9965 100644 --- a/example/simple_chat_screen.hpp +++ b/example/simple_chat_screen.hpp @@ -5,7 +5,7 @@ * @author Thurman Gillespy * * Copyright (c) 2019 Thurman Gillespy - * 4/14/19 + * 4/15/19 * * 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) @@ -102,7 +102,7 @@ class simple_chat_screen { "|---------------------------------------------------------------------------|\n"; S HDR_START = "| "; - S PROMPT = "> "; + S PROMPT = " > "; // create the string that represent the (unchanging) upper screen so only calculate once void create_upper_screen(bool print_err) { From c737bdddcf22b601ab4c42f767030137dacfde84 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Mon, 15 Apr 2019 12:23:56 -0700 Subject: [PATCH 15/17] sta.send() now to all clients (don't use send_to_all overload) --- example/chat_server_demo.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/example/chat_server_demo.cpp b/example/chat_server_demo.cpp index 0a1cf51e..fbb08103 100644 --- a/example/chat_server_demo.cpp +++ b/example/chat_server_demo.cpp @@ -7,7 +7,7 @@ * @author Thurman Gillespy * * Copyright (c) 2019 Thurman Gillespy - * 4/14/19 + * 4/15/19 * * 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) @@ -112,7 +112,8 @@ int main(int argc, char *argv[]) /* lamda handlers */ // receive text from client, send out to others const auto msg_hndlr = [&sta](const_buf buf, io_interface iof, endpoint ep) { - sta.send(buf.data(), buf.size(), iof); + // sta.send(buf.data(), buf.size(), iof); + sta.send(buf.data(), buf.size()); return true; }; From 4c55b0e6d06e91070bd544071e36cbcc4d27d2c0 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Mon, 15 Apr 2019 12:27:23 -0700 Subject: [PATCH 16/17] msg_hndlr: return false on flag; don't print local message --- example/simple_chat_demo.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/example/simple_chat_demo.cpp b/example/simple_chat_demo.cpp index a8b23760..ff6327fb 100644 --- a/example/simple_chat_demo.cpp +++ b/example/simple_chat_demo.cpp @@ -197,7 +197,9 @@ int main(int argc, char* argv[]) { std::string ip_addr; std::string port; std::string param; + std::string last_msg; bool print_errors = false; + bool shutdown = false; std::promise promise_obj; std::future future_obj = promise_obj.get_future(); @@ -212,14 +214,24 @@ int main(int argc, char* argv[]) { // message handler for @c network_entity auto msg_hndlr = [&] (const_buf buf, io_interface iof, endpoint ep) { + if (shutdown) { + screen.insert_scroll_line("msg_hndlr shutdown" + DELIM, + SYSTEM); + screen.draw_screen(); + + return false; + } + // receive data from acceptor, display to user // remove deliminator here? std::string s (static_cast (buf.data()), buf.size()); - screen.insert_scroll_line(s, REMOTE); - screen.draw_screen(); - - // return false if user entered 'quit', otherwise true - return s == "quit" + DELIM ? false : true; + + if (s != last_msg) { + screen.insert_scroll_line(s, REMOTE); + screen.draw_screen(); + } + + return true; }; // handler for @c tcp_connector @@ -288,13 +300,19 @@ int main(int argc, char* argv[]) { // io state change handler tcp_io_interface tcp_iof; // used to send text data tcp_iof = future_obj.get(); // wait for value set in io_state_chng_hndlr + // get std::string from user, send string data over network, update screen std::string s; - while (s != "quit" + DELIM) { + // while (s != "quit" + DELIM) { + while (!shutdown) { std::getline (std::cin, s); // user input + if (s == "quit") { + shutdown = true; + } s += DELIM; // needed for deliminator screen.insert_scroll_line(s, LOCAL); screen.draw_screen(); + last_msg = s; if (!tcp_iof.is_valid()) { screen.insert_scroll_line(NO_CONNECTION, SYSTEM); screen.draw_screen(); @@ -302,6 +320,7 @@ int main(int argc, char* argv[]) { } // note correct method of sending string data over network tcp_iof.send(s.data(), s.size()); + } // allow last message to be sent before shutting down connection std::this_thread::sleep_for(std::chrono::seconds(1)); From 67387aa9acfd3c37463cb4cf3443cdd631d2dfd2 Mon Sep 17 00:00:00 2001 From: Thurman Gillespy Date: Mon, 15 Apr 2019 13:56:00 -0700 Subject: [PATCH 17/17] simple change to PROMPT --- example/simple_chat_screen.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/simple_chat_screen.hpp b/example/simple_chat_screen.hpp index 0ccb9965..50309fb7 100644 --- a/example/simple_chat_screen.hpp +++ b/example/simple_chat_screen.hpp @@ -102,7 +102,7 @@ class simple_chat_screen { "|---------------------------------------------------------------------------|\n"; S HDR_START = "| "; - S PROMPT = " > "; + S PROMPT = "| > "; // create the string that represent the (unchanging) upper screen so only calculate once void create_upper_screen(bool print_err) {