Skip to content

Instantly share code, notes, and snippets.

@tgill880
Created May 29, 2019 07:02
Show Gist options
  • Save tgill880/177236582fb1c80f7e3cdc039f5bd10c to your computer and use it in GitHub Desktop.
Save tgill880/177236582fb1c80f7e3cdc039f5bd10c to your computer and use it in GitHub Desktop.
chops-net-ip echo server demo program
/**
* @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