Created
May 29, 2019 07:02
-
-
Save tgill880/177236582fb1c80f7e3cdc039f5bd10c to your computer and use it in GitHub Desktop.
chops-net-ip echo server demo program
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file | |
* | |
* @ingroup example_module | |
* | |
* @brief TCP acceptor (server) that receives binary text messages, converts | |
* to upper case, then echos back to TCP connector (client). | |
* | |
* @note ESP32 port of echo_binary_text_server_demo.cpp. | |
* | |
* @author Thurman Gillespy | |
* | |
* Copyright (c) Thurman Gillespy | |
* 5/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) | |
* | |
* | |
*/ | |
// workaround to get this code to compile | |
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | |
// ESP32 (from asio example) | |
#include <cstdlib> | |
#include <deque> | |
#include <iostream> | |
#include <list> | |
#include <memory> | |
#include <set> | |
#include <utility> | |
// #include "asio.hpp" | |
#include "protocol_examples_common.h" | |
#include "esp_event.h" | |
#include "tcpip_adapter.h" | |
#include "nvs_flash.h" | |
// chops-net-ip | |
#include <cstddef> // std::size_t, std::byte | |
#include <string> | |
#include <chrono> | |
#include <thread> | |
#include <cassert> | |
#include "net_ip/net_ip.hpp" | |
#include "net_ip/basic_net_entity.hpp" | |
#include "net_ip/component/worker.hpp" | |
#include "marshall/extract_append.hpp" | |
#include <net/if.h> // if_indextoname | |
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; | |
//---------------------------------------------------------------------- | |
extern "C" void app_main() | |
{ | |
ESP_ERROR_CHECK(nvs_flash_init()); | |
tcpip_adapter_init(); | |
ESP_ERROR_CHECK(esp_event_loop_create_default()); | |
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. | |
* Read "Establishing Wi-Fi or Ethernet Connection" section in | |
* examples/protocols/README.md for more information about this function. | |
*/ | |
ESP_ERROR_CHECK(example_connect()); | |
/* This helper function configures blocking UART I/O */ | |
ESP_ERROR_CHECK(example_configure_stdin_stdout()); | |
const std::size_t HDR_SIZE = 2; // 1st 2 bytes of data is message size | |
const std::string PORT = CONFIG_EXAMPLE_PORT; | |
bool hdr_processed = false; | |
bool print_errors = true; | |
/**** 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 received text to uppercase | |
auto to_upper = [](char &c) { c = ::toupper(c); }; | |
std::for_each(s.begin(), s.end(), to_upper); | |
// create buffer to send altered text back to client | |
chops::mutable_shared_buffer buf_out; | |
// 1st 2 bytes are the size of the message | |
uint16_t size_val = s.size(); | |
// 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<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 back to the client | |
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 | |
// endian correct data marshalling | |
uint16_t size = chops::extract_val<uint16_t>(static_cast<std::byte *>(buf.data())); | |
return size; // return the size of the text data (obtained from header) | |
} | |
}; | |
// 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); | |
// begin | |
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(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment