// // chat_client.cpp // ~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include <cstdlib> #include <deque> #include <iostream> #include <thread> #include "asio.hpp" #include "chat_message.hpp" #include "protocol_examples_common.h" #include "esp_event.h" #include "tcpip_adapter.h" #include "nvs_flash.h" using asio::ip::tcp; typedef std::deque<chat_message> chat_message_queue; class chat_client { public: chat_client(asio::io_context& io_context, const tcp::resolver::results_type& endpoints) : io_context_(io_context), socket_(io_context) { do_connect(endpoints); } void write(const chat_message& msg) { asio::post(io_context_, [this, msg]() { bool write_in_progress = !write_msgs_.empty(); write_msgs_.push_back(msg); if (!write_in_progress) { do_write(); } }); } void close() { asio::post(io_context_, [this]() { socket_.close(); }); } private: void do_connect(const tcp::resolver::results_type& endpoints) { asio::async_connect(socket_, endpoints, [this](std::error_code ec, tcp::endpoint) { if (!ec) { do_read_header(); } }); } void do_read_header() { asio::async_read(socket_, asio::buffer(read_msg_.data(), chat_message::header_length), [this](std::error_code ec, std::size_t /*length*/) { if (!ec && read_msg_.decode_header()) { do_read_body(); } else { socket_.close(); } }); } void do_read_body() { asio::async_read(socket_, asio::buffer(read_msg_.body(), read_msg_.body_length()), [this](std::error_code ec, std::size_t /*length*/) { if (!ec) { std::cout.write(read_msg_.body(), read_msg_.body_length()); std::cout << "\n"; do_read_header(); } else { socket_.close(); } }); } void do_write() { asio::async_write(socket_, asio::buffer(write_msgs_.front().data(), write_msgs_.front().length()), [this](std::error_code ec, std::size_t /*length*/) { if (!ec) { write_msgs_.pop_front(); if (!write_msgs_.empty()) { do_write(); } } else { socket_.close(); } }); } private: asio::io_context& io_context_; tcp::socket socket_; chat_message read_msg_; chat_message_queue write_msgs_; }; void read_line(char * line, int max_chars); extern "C" void app_main(void) { 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()); std::string name(CONFIG_EXAMPLE_SERVER_IP); std::string port(CONFIG_EXAMPLE_PORT); char line[chat_message::max_body_length + 1] = { 0 }; if (name == "FROM_STDIN") { std::cout << "Please enter ip address of chat server" << std::endl; if (std::cin.getline(line, chat_message::max_body_length + 1)) { name = line; std::cout << "Chat server IP:" << name << std::endl; } } asio::io_context io_context; tcp::resolver resolver(io_context); auto endpoints = resolver.resolve(name, port); chat_client c(io_context, endpoints); std::thread t([&io_context](){ io_context.run(); }); while (std::cin.getline(line, chat_message::max_body_length + 1) && std::string(line) != "exit") { chat_message msg; msg.body_length(std::strlen(line)); std::memcpy(msg.body(), line, msg.body_length()); msg.encode_header(); c.write(msg); } c.close(); t.join(); ESP_ERROR_CHECK(example_disconnect()); }