mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-29 02:07:27 +02:00
examples: Asio tests consolidation to use loopback interface
Closes IDF-3072 * Original commit: espressif/esp-idf@a06357ab62
This commit is contained in:
committed by
gabsuren
parent
cb2375827b
commit
91262baede
10
examples/protocols/asio/asio_chat/CMakeLists.txt
Normal file
10
examples/protocols/asio/asio_chat/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_chat)
|
9
examples/protocols/asio/asio_chat/Makefile
Normal file
9
examples/protocols/asio/asio_chat/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
PROJECT_NAME := asio_chat
|
||||
|
||||
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
62
examples/protocols/asio/asio_chat/README.md
Normal file
62
examples/protocols/asio/asio_chat/README.md
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
# Asio chat client and server examples
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
The application aims to demonstrate a simple use of Asio library in different modes.
|
||||
In project settings it could be configured to run either a Asio chat server, a Asio chat client, or both.
|
||||
|
||||
## How to use example
|
||||
|
||||
The example is configured by default as an Asio chat client.
|
||||
|
||||
Note that the example uses string representation of IP addresses and ports.
|
||||
|
||||
You can find the upstream asio chat implementation [here] https://github.com/chriskohlhoff/asio/tree/master/asio/src/examples/cpp11/chat
|
||||
|
||||
### Asio Client
|
||||
|
||||
In the client mode, the example connects to the configured address, sends the message, which was inserted as an input in the terminal, and receives a response.
|
||||
|
||||
### Asio Server
|
||||
|
||||
In the server mode, Asio chat server with a specified port number is created and being polled till a connection request from the client arrives.
|
||||
Chat server echoes a message (received from any client) to all connected clients.
|
||||
|
||||
## Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
Set following parameters under Example Configuration Options:
|
||||
|
||||
* Set `EXAMPLE_CHAT_SERVER` to use the example as an ASIO chat server
|
||||
* Configure `EXAMPLE_CHAT_SERVER_BIND_PORT` to the port number.
|
||||
|
||||
* Set `EXAMPLE_CHAT_CLIENT` to use the example as an ASIO chat client
|
||||
* Configure `EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS` to a string representation of the address to connect the client to.
|
||||
* Configure `EXAMPLE_CHAT_CLIENT_CONNECT_PORT` to the port number.
|
||||
|
||||
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more d etails.
|
||||
|
||||
## Running the example in server mode
|
||||
|
||||
- Configure the example according "Configure the project" section.
|
||||
- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal.
|
||||
- Wait for the board to connect to WiFi or Ethernet (note the IP address).
|
||||
- Connect to the server using multiple clients, for example using any option below.
|
||||
- build and run asio chat client on your host machine
|
||||
- run chat_client asio example on ESP platform
|
||||
- since chat messages consists of ASCII size and message, it is possible to
|
||||
netcat `nc IP PORT` and type for example ` 4ABC<CR>` to transmit 'ABC\n'
|
||||
|
||||
## Running the example in client mode
|
||||
|
||||
- Configure the example according "Configure the project" section.
|
||||
- Start chat server either on host machine or as another ESP device running chat_server example.
|
||||
- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal.
|
||||
- Wait for the board to connect to WiFi or Ethernet.
|
||||
- Receive and send messages to/from other clients on stdin/stdout via serial terminal.
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
29
examples/protocols/asio/asio_chat/example_test.py
Normal file
29
examples/protocols/asio/asio_chat/example_test.py
Normal file
@ -0,0 +1,29 @@
|
||||
# This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, this
|
||||
# software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_asio_chat(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
|
||||
msg = 'asio-chat: received hi'
|
||||
dut = env.get_dut('asio_chat', 'examples/protocols/asio/asio_chat')
|
||||
# start the test and expect the client to receive back it's original data
|
||||
dut.start_app()
|
||||
dut.expect(re.compile(r'{}'.format('Waiting for input')), timeout=30)
|
||||
dut.write(msg)
|
||||
dut.write('exit')
|
||||
dut.expect(re.compile(r'{}'.format(msg)), timeout=30)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_asio_chat()
|
2
examples/protocols/asio/asio_chat/main/CMakeLists.txt
Normal file
2
examples/protocols/asio/asio_chat/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "asio_chat.cpp"
|
||||
INCLUDE_DIRS ".")
|
39
examples/protocols/asio/asio_chat/main/Kconfig.projbuild
Normal file
39
examples/protocols/asio/asio_chat/main/Kconfig.projbuild
Normal file
@ -0,0 +1,39 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_CHAT_SERVER
|
||||
bool "Asio example chat server"
|
||||
default n
|
||||
help
|
||||
This example will setup a chat server, binds it to the specified address
|
||||
and starts listening.
|
||||
|
||||
if EXAMPLE_CHAT_SERVER
|
||||
config EXAMPLE_CHAT_SERVER_BIND_PORT
|
||||
string "Asio example server bind port"
|
||||
default "3344"
|
||||
help
|
||||
Server listener's socket would be bound to this port.
|
||||
endif
|
||||
|
||||
config EXAMPLE_CHAT_CLIENT
|
||||
bool "Asio example chat client"
|
||||
default y
|
||||
help
|
||||
This example will setup an asio chat client.
|
||||
and sends the data.
|
||||
|
||||
if EXAMPLE_CHAT_CLIENT
|
||||
config EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS
|
||||
string "Client connection address"
|
||||
default "192.168.0.1"
|
||||
help
|
||||
Client's socket would connect to this address/host.
|
||||
|
||||
config EXAMPLE_CHAT_CLIENT_CONNECT_PORT
|
||||
string "Client connection port"
|
||||
default "3344"
|
||||
help
|
||||
Client's connection port.
|
||||
endif
|
||||
|
||||
endmenu
|
119
examples/protocols/asio/asio_chat/main/asio_chat.cpp
Normal file
119
examples/protocols/asio/asio_chat/main/asio_chat.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
/* ASIO chat server client example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "server.hpp"
|
||||
#include "client.hpp"
|
||||
#include <thread>
|
||||
#include <pthread.h>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
static const char *TAG = "asio-chat";
|
||||
|
||||
// This variable is necessary for `python test` execution, it provides synchronisation between server/client(as server should be started before client)
|
||||
std::mutex server_ready;
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_CLIENT
|
||||
static void get_string(char *line, size_t size)
|
||||
{
|
||||
int count = 0;
|
||||
while (count < size) {
|
||||
int c = fgetc(stdin);
|
||||
if (c == '\n') {
|
||||
line[count] = '\0';
|
||||
break;
|
||||
} else if (c > 0 && c < 127) {
|
||||
line[count] = c;
|
||||
++count;
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void start_client(void)
|
||||
{
|
||||
const std::string port(CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_PORT);
|
||||
const std::string name(CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS);
|
||||
asio::io_context io_context;
|
||||
char line[128];
|
||||
|
||||
tcp::resolver resolver(io_context);
|
||||
auto endpoints = resolver.resolve(name, port);
|
||||
chat_client c(io_context, endpoints);
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_SERVER
|
||||
std::lock_guard<std::mutex> guard(server_ready);
|
||||
#endif
|
||||
std::thread t([&io_context]() { try {
|
||||
io_context.run();
|
||||
} catch (const std::exception &e) {
|
||||
ESP_LOGE(TAG, "Exception occured during client thread execution %s", e.what());
|
||||
}
|
||||
catch (...) {
|
||||
ESP_LOGE(TAG, "Unknown exception");
|
||||
}});
|
||||
do {
|
||||
ESP_LOGI(TAG, "CLIENT: Waiting for input");
|
||||
get_string(line, sizeof(line));
|
||||
|
||||
chat_message msg;
|
||||
msg.body_length(std::strlen(line));
|
||||
std::memcpy(msg.body(), line, msg.body_length());
|
||||
msg.encode_header();
|
||||
c.write(msg);
|
||||
sleep(1);
|
||||
} while (strcmp(line, "exit") != 0);
|
||||
|
||||
c.close();
|
||||
t.join();
|
||||
}
|
||||
#endif // CONFIG_EXAMPLE_CHAT_CLIENT
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
esp_netif_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());
|
||||
|
||||
try {
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_SERVER
|
||||
asio::io_context io_context;
|
||||
chat_server server(io_context, tcp::endpoint(tcp::v4(), std::atoi(CONFIG_EXAMPLE_CHAT_SERVER_BIND_PORT)));
|
||||
std::thread t = std::thread([&io_context]() { // Chat server starting here
|
||||
try {
|
||||
io_context.run();
|
||||
} catch (const std::exception &e) {
|
||||
ESP_LOGE(TAG, "Exception occured during server thread execution %s", e.what());
|
||||
}
|
||||
catch (...) {
|
||||
ESP_LOGE(TAG, "Unknown exception");
|
||||
}});;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_CLIENT
|
||||
start_client();
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_SERVER
|
||||
t.join();
|
||||
#endif
|
||||
} catch (const std::exception &e) {
|
||||
ESP_LOGE(TAG, "Exception occured during run %s", e.what());
|
||||
} catch (...) {
|
||||
ESP_LOGE(TAG, "Unknown exception");
|
||||
}
|
||||
ESP_ERROR_CHECK(example_disconnect());
|
||||
}
|
91
examples/protocols/asio/asio_chat/main/chat_message.hpp
Normal file
91
examples/protocols/asio/asio_chat/main/chat_message.hpp
Normal file
@ -0,0 +1,91 @@
|
||||
//
|
||||
// chat_message.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef CHAT_MESSAGE_HPP
|
||||
#define CHAT_MESSAGE_HPP
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
class chat_message
|
||||
{
|
||||
public:
|
||||
enum { header_length = 4 };
|
||||
enum { max_body_length = 512 };
|
||||
|
||||
chat_message()
|
||||
: body_length_(0)
|
||||
{
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
char* data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
std::size_t length() const
|
||||
{
|
||||
return header_length + body_length_;
|
||||
}
|
||||
|
||||
const char* body() const
|
||||
{
|
||||
return data_ + header_length;
|
||||
}
|
||||
|
||||
char* body()
|
||||
{
|
||||
return data_ + header_length;
|
||||
}
|
||||
|
||||
std::size_t body_length() const
|
||||
{
|
||||
return body_length_;
|
||||
}
|
||||
|
||||
void body_length(std::size_t new_length)
|
||||
{
|
||||
body_length_ = new_length;
|
||||
if (body_length_ > max_body_length)
|
||||
body_length_ = max_body_length;
|
||||
}
|
||||
|
||||
bool decode_header()
|
||||
{
|
||||
char header[header_length + 1] = "";
|
||||
std::strncat(header, data_, header_length);
|
||||
body_length_ = std::atoi(header);
|
||||
if (body_length_ > max_body_length)
|
||||
{
|
||||
body_length_ = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encode_header()
|
||||
{
|
||||
char header[header_length + 1] = "";
|
||||
std::sprintf(header, "%4d", static_cast<int>(body_length_));
|
||||
std::memcpy(data_, header, header_length);
|
||||
}
|
||||
|
||||
private:
|
||||
char data_[header_length + max_body_length];
|
||||
std::size_t body_length_;
|
||||
};
|
||||
|
||||
#endif // CHAT_MESSAGE_HPP
|
126
examples/protocols/asio/asio_chat/main/client.hpp
Normal file
126
examples/protocols/asio/asio_chat/main/client.hpp
Normal file
@ -0,0 +1,126 @@
|
||||
//
|
||||
// client.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef CHAT_CLIENT_HPP
|
||||
#define CHAT_CLIENT_HPP
|
||||
|
||||
#include <deque>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
class chat_client
|
||||
{
|
||||
public:
|
||||
chat_client(asio::io_context& io_context,
|
||||
const asio::ip::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 asio::ip::tcp::resolver::results_type& endpoints)
|
||||
{
|
||||
asio::async_connect(socket_, endpoints,
|
||||
[this](std::error_code ec, asio::ip::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)
|
||||
{
|
||||
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_;
|
||||
asio::ip::tcp::socket socket_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
#endif // CHAT_CLIENT_HPP
|
8
examples/protocols/asio/asio_chat/main/component.mk
Normal file
8
examples/protocols/asio/asio_chat/main/component.mk
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# Main component makefile.
|
||||
#
|
||||
# This Makefile can be left empty. By default, it will take the sources in the
|
||||
# src/ directory, compile them and link them into lib(subdirectory_name).a
|
||||
# in the build directory. This behaviour is entirely configurable,
|
||||
# please read the ESP-IDF documents if you need to do this.
|
||||
#
|
202
examples/protocols/asio/asio_chat/main/server.hpp
Normal file
202
examples/protocols/asio/asio_chat/main/server.hpp
Normal file
@ -0,0 +1,202 @@
|
||||
//
|
||||
// server.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef CHAT_SERVER_HPP
|
||||
#define CHAT_SERVER_HPP
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
extern std::mutex server_ready;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
|
||||
class chat_participant
|
||||
{
|
||||
public:
|
||||
virtual ~chat_participant() {}
|
||||
virtual void deliver(const chat_message& msg) = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<chat_participant> chat_participant_ptr;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_room
|
||||
{
|
||||
public:
|
||||
void join(chat_participant_ptr participant)
|
||||
{
|
||||
participants_.insert(participant);
|
||||
for (auto msg: recent_msgs_)
|
||||
participant->deliver(msg);
|
||||
}
|
||||
|
||||
void leave(chat_participant_ptr participant)
|
||||
{
|
||||
participants_.erase(participant);
|
||||
}
|
||||
|
||||
void deliver(const chat_message& msg)
|
||||
{
|
||||
recent_msgs_.push_back(msg);
|
||||
while (recent_msgs_.size() > max_recent_msgs)
|
||||
recent_msgs_.pop_front();
|
||||
|
||||
for (auto participant: participants_)
|
||||
participant->deliver(msg);
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<chat_participant_ptr> participants_;
|
||||
enum { max_recent_msgs = 100 };
|
||||
chat_message_queue recent_msgs_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
|
||||
class chat_session
|
||||
: public chat_participant,
|
||||
public std::enable_shared_from_this<chat_session>
|
||||
{
|
||||
public:
|
||||
chat_session(asio::ip::tcp::socket socket, chat_room& room)
|
||||
: socket_(std::move(socket)),
|
||||
room_(room)
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
room_.join(shared_from_this());
|
||||
do_read_header();
|
||||
}
|
||||
|
||||
void deliver(const chat_message& msg)
|
||||
{
|
||||
bool write_in_progress = !write_msgs_.empty();
|
||||
write_msgs_.push_back(msg);
|
||||
if (!write_in_progress)
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void do_read_header()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec && read_msg_.decode_header())
|
||||
{
|
||||
do_read_body();
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_body()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
ESP_LOGD("asio-chat:", "%s", read_msg_.body());
|
||||
room_.deliver(read_msg_);
|
||||
do_read_header();
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
asio::ip::tcp::socket socket_;
|
||||
chat_room& room_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_server
|
||||
{
|
||||
public:
|
||||
chat_server(asio::io_context& io_context,
|
||||
const asio::ip::tcp::endpoint& endpoint)
|
||||
: acceptor_(io_context, endpoint)
|
||||
{
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(server_ready);
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, asio::ip::tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<chat_session>(std::move(socket), room_)->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
asio::ip::tcp::acceptor acceptor_;
|
||||
chat_room room_;
|
||||
};
|
||||
|
||||
#endif // CHAT_SERVER_HPP
|
6
examples/protocols/asio/asio_chat/sdkconfig.ci
Normal file
6
examples/protocols/asio/asio_chat/sdkconfig.ci
Normal file
@ -0,0 +1,6 @@
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
|
||||
CONFIG_EXAMPLE_CHAT_CLIENT=y
|
||||
CONFIG_EXAMPLE_CHAT_SERVER=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS="localhost"
|
2
examples/protocols/asio/asio_chat/sdkconfig.defaults
Normal file
2
examples/protocols/asio/asio_chat/sdkconfig.defaults
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
Reference in New Issue
Block a user