Skip to content
Merged

Tg #26

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 205 additions & 0 deletions example/echo_binary_text_client_demo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/**
* @file
*
* @ingroup example_module
*
* @brief TCP connector (client) that sends binary text message to
* server, receives message back converted to upper case.
*
* @author Thurman Gillespy
*
* Copyright (c) Thurman Gillespy
* 4/25/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/ \
echo_binary_text_client_demo.cpp -lpthread -o echo_client
*
*/

#include <iostream>
#include <cstdlib> // EXIT_SUCCESS
#include <cstddef> // std::size_t
#include <string>
#include <thread>
#include <chrono>
#include <cassert>

#include "net_ip/net_ip.hpp"
#include "net_ip/basic_net_entity.hpp"
#include "net_ip/component/worker.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 (if any)
bool process_args(int argc, char* argv[], bool& print_errors, std::string& ip_address,
std::string& port) {
const std::string HELP = "-h";
const std::string PRINT_ERRS = "-e";
const std::string usage = \
"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 port: 5002\n"
" change port and use local loop:\n"
" ./echo_client [e] \"\" port";

int offset = 0;

if (argc > 4 || (argc > 1 && argv[1] == HELP)) {
std::cout << usage << 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 << usage << std::endl;
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}


int main(int argc, char* argv[]) {
const std::size_t HDR_SIZE = 2; // 1st 2 bytes of header is message size
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_interface iof, endpoint ep) {
// create string from buf, omit 1st 2 bytes (header)
std::string s (static_cast<const 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
uint16_t size = *(static_cast<uint16_t*> (buf.data()));

return size;
}
};

// io state change handler
auto io_state_chng_hndlr = [&msg_hndlr, &msg_frame, &tcp_iof]
(io_interface iof, std::size_t n, bool flag) {

if (flag) {
iof.start_io(HDR_SIZE, msg_hndlr, msg_frame);
tcp_iof = iof; // return iof to main, used later to send text
} else {
iof.stop_io();
}

};

// 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());

// creae a network entity
chops::net::tcp_connector_net_entity net_entity_connect;
net_entity_connect = echo_client.make_tcp_connector(port.c_str(), ip_address.c_str(),
std::chrono::milliseconds(5000));
assert(net_entity_connect.is_valid());
// start network entity, emplace handlers
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;
}
// 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;

// 1st 2 bytes size of message are the string length
uint16_t size_val = s.size();
buf.append(&size_val, sizeof(size_val)); // put 2 bytes into buffer
buf.append(s.data(), s.size()); // now add the string
// send message to server (TCP_acceptor)
tcp_iof.send(buf.data(), buf.size());
}

// cleanup
net_entity_connect.stop();
wk.stop();

return EXIT_SUCCESS;
}
175 changes: 175 additions & 0 deletions example/echo_binary_text_server_demo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/**
* @file
*
* @ingroup example_module
*
* @brief TCP acceptor (server) that receives binary text messages, converts
* to upper case, then echos back to TCP connector (client).
*
* @author Thurman Gillespy
*
* Copyright (c) Thurman Gillespy
* 4/25/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/ \
echo_binary_text_server_demo.cpp -lpthread -o echo_server
*
*/

#include <iostream>
#include <cstdlib> // EXIT_SUCCESS
#include <cstddef> // std::size_t
#include <string>
#include <thread>
#include <cassert>

#include "net_ip/net_ip.hpp"
#include "net_ip/basic_net_entity.hpp"
#include "net_ip/component/worker.hpp"
#include "asio.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;

bool processArgs(int argc, char* argv[], bool& print_errors, std::string& port) {
const std::string HELP = "-h";
const std::string PRINT_ERRS = "-e";
const std::string usage = \
"useage: ./echo_server [-h | -e] [port]\n"
" -h Print useage\n"
" -e Print error messages\n"
" port Default port: 5002";
int offset = 0;

if (argc > 3 || (argc > 1 && argv[1] == HELP)) {
std::cout << usage << std::endl;
return EXIT_FAILURE;
}

if (argc > 1 && argv[1] == PRINT_ERRS) {
print_errors = true;
offset = 1;
}

if (argc > 1 + offset) {
port = argv[1 + offset];
}

return EXIT_SUCCESS;
}

int main(int argc, char* argv[]) {
const std::size_t HDR_SIZE = 2; // 1st 2 bytes of header is message size
const std::string PORT = "5002";

std::string port = PORT;
bool hdr_processed = false;
bool print_errors = false;

if (processArgs(argc, argv, print_errors, port) == EXIT_FAILURE) {
return EXIT_FAILURE;
}

/**** lambda handlers ****/

// message handler
// receive text, convert to uppercase, send back to client
auto msg_hndlr = [] (const_buf buf, io_interface iof, endpoint ep) {
// create string from buf, omit 1st 2 bytes (header)
std::string s (static_cast<const char*> (buf.data()) + 2, buf.size() - 2);
// print info about client
std::cout << "received request from " << ep.address() << ":" << ep.port() << std::endl;
std::cout << " text: " << s << std::endl;
// convert to uppercase
auto to_upper = [] (char& c) { c = ::toupper(c); };
std::for_each(s.begin(), s.end(), to_upper);

// create buffer to send test data
chops::mutable_shared_buffer buf_out;
// 1st 2 bytes are the size of the message
uint16_t size_val = s.size();
buf_out.append(&size_val, sizeof(size_val)); // write 2 byte size
buf_out.append(s.data(), s.size()); // now add the text data

iof.send(buf_out.data(), buf_out.size());

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
uint16_t size = *(static_cast<uint16_t*> (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) {
iof.start_io(HDR_SIZE, msg_hndlr, msg_frame);
} else {
iof.stop_io();
}

};

// error handler
auto err_func = [&] (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_server(wk.get_io_context());
chops::net::tcp_acceptor_net_entity net_entity_accept;

// make @ tcp_acceptor, return @c network_entity
net_entity_accept = echo_server.make_tcp_acceptor(port.c_str());
assert(net_entity_accept.is_valid());
// start network entity, emplace handlers
net_entity_accept.start(io_state_chng_hndlr, err_func);

std::cout << "chops-net-ip binary text echo demo - server" << std::endl;
std::cout << " IP address:port = 127.0.0.1:" << port << std::endl;
std::cout << " print error messages: " << (print_errors ? "ON" : "OFF") << std::endl;
std::cout << "Press return to exit" << std::endl;

std::string s;
std::getline(std::cin, s); // pause until return

// cleanup
net_entity_accept.stop();
wk.stop();

return EXIT_SUCCESS;
}