ASIO2 异步网络通信库嵌入式 Linux C++ 使用指南

文档信息

项目内容
适用场景ARM架构Linux系统、嵌入式C++网络/串口开发、异步通信场景
前置依赖C++11/14编译器(arm-linux-gnueabihf-gcc 7.x+)、CMake 3.10+、pthread库

一、工具介绍

1.1 工具定位

ASIO2是一款基于boost.asio(也支持独立版asio)封装的轻量级异步网络库,专为高性能、低资源占用设计,适配嵌入式Linux场景下的TCP/UDP网络通信、串口通信需求,相比原生asio大幅简化API调用,降低嵌入式网络开发门槛。

1.2 核心特性

  • 零冗余封装:保留asio核心异步非阻塞特性,无多余依赖,编译后体积<500KB;
  • 多通信模式:支持TCP客户端/服务端、UDP单播/组播、串口异步通信(嵌入式高频场景);
  • 断线重连:内置简洁的重连机制,适配嵌入式网络不稳定的工业场景;
  • 线程安全:原生支持多线程调度,适配嵌入式多核CPU(如Cortex-A7/A53);
  • 低资源占用:内存占用<100KB,CPU占用率<5%(1000次/秒通信频率);
  • 交叉编译友好:纯源码集成,无需预编译库,适配ARM/Linux交叉编译链。

1.3 嵌入式场景对比优势

对比项ASIO2原生boost.asio
依赖体积仅集成核心asio代码(≈200KB)完整boost库(≈10MB)
API复杂度极简封装(一行启动服务端/客户端)需手动管理io_context、socket等
嵌入式适配支持串口/低功耗模式无串口封装,需手动适配
交叉编译无需修改源码,直接编译需裁剪boost库,配置复杂

二、快速集成(嵌入式推荐源码集成)

2.1 源码下载

# 克隆ASIO2源码(指定稳定版v2.3.2)
git clone https://github.com/zhllxt/asio2.git
cd asio2 && git checkout v2.3.2
# 仅保留核心文件(嵌入式精简)
cp -r asio2/include/asio2 /your/project/include/
cp -r asio2/3rd/asio/include/asio /your/project/include/  # 独立asio依赖

2.2 CMakeLists.txt配置(交叉编译适配)

# 基础配置
cmake_minimum_required(VERSION 3.10)
project(embedded_asio2_demo)

# 指定C++标准(嵌入式推荐C++11/14)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 交叉编译工具链(按需替换为你的工具链路径)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)

# 添加头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)

# 链接线程库(asio2依赖pthread)
link_directories(/usr/arm-linux-gnueabihf/lib)

# 编译可执行文件
add_executable(asio2_demo src/main.cpp)
target_link_libraries(asio2_demo pthread)

# 嵌入式优化:关闭异常(可选,减少体积)
add_compile_definitions(ASIO_NO_EXCEPTIONS)

2.3 验证集成(最简TCP服务端示例)

测试代码(src/main.cpp)
#include "asio2/asio2.hpp"

int main() {
    // 创建TCP服务端,监听8080端口
    asio2::tcp_server server;

    // 注册连接回调:客户端连接成功时触发
    server.set_accept_handler([&](auto& session) {
        printf("客户端[%s]已连接\n", session.remote_address().c_str());
    });

    // 注册数据接收回调:收到客户端数据时触发
    server.set_recv_handler([&](auto& session, std::string_view data) {
        printf("收到数据:%.*s\n", (int)data.size(), data.data());
        // 回显数据给客户端
        session.send(data);
    });

    // 启动服务端(绑定本地所有网卡,端口8080)
    server.start("0.0.0.0", 8080);

    // 阻塞主线程(嵌入式可替换为业务循环)
    while (asio2::main_run()) {}

    return 0;
}
交叉编译&运行
# 编译
mkdir build && cd build
cmake .. && make
# 拷贝到嵌入式设备(以SSH为例)
scp asio2_demo root@192.168.1.100:/root/
# 嵌入式设备运行
./asio2_demo
# 测试:在PC端用telnet连接
telnet 192.168.1.100 8080

三、基础使用

3.1 TCP通信

3.1.1 TCP客户端(主动上报数据)
#include "asio2/asio2.hpp"
#include <unistd.h>  // 嵌入式延时头文件

int main() {
    // 创建TCP客户端
    asio2::tcp_client client;

    // 注册连接成功回调
    client.set_connect_handler([&]() {
        printf("连接服务端成功\n");
    });

    // 注册断线重连回调
    client.set_reconnect_handler([&]() {
        printf("断线,正在重连...\n");
        return true;  // 返回true表示继续重连
    });

    // 启动客户端(连接远程服务端,开启自动重连)
    client.start("192.168.1.200", 8080, asio2::reconnect::enable);

    // 嵌入式业务循环:每隔1秒上报设备状态
    while (asio2::main_run()) {
        std::string status = "temp:25.5,hum:60,volt:12.3";
        if (client.is_connected()) {
            client.send(status);  // 发送数据
        }
        sleep(1);  // 嵌入式延时(替代std::this_thread::sleep_for)
    }

    return 0;
}
3.1.2 TCP服务端(接收多客户端数据)
#include "asio2/asio2.hpp"
#include <map>

// 存储客户端会话(嵌入式多客户端管理)
std::map<std::string, asio2::tcp_session_ptr> clients;

int main() {
    asio2::tcp_server server;

    // 客户端连接:记录会话
    server.set_accept_handler([&](auto& session) {
        std::string client_id = session.remote_address() + ":" + std::to_string(session.remote_port());
        clients[client_id] = session.shared_from_this();
        printf("客户端[%s]连接,当前在线:%lu\n", client_id.c_str(), clients.size());
    });

    // 客户端断开:移除会话
    server.set_disconnect_handler([&](auto& session) {
        std::string client_id = session.remote_address() + ":" + std::to_string(session.remote_port());
        clients.erase(client_id);
        printf("客户端[%s]断开,当前在线:%lu\n", client_id.c_str(), clients.size());
    });

    // 接收数据:解析嵌入式设备上报的状态
    server.set_recv_handler([&](auto& session, std::string_view data) {
        printf("客户端[%s]上报:%.*s\n", session.remote_address().c_str(), (int)data.size(), data.data());
    });

    // 启动服务端(嵌入式建议绑定内网IP,避免暴露)
    server.start("192.168.1.100", 8080);

    while (asio2::main_run()) {}

    return 0;
}

3.2 UDP通信(低延迟场景)

3.2.1 UDP服务端(监听广播/单播)
#include "asio2/asio2.hpp"

int main() {
    asio2::udp_server server;

    // 接收UDP数据回调
    server.set_recv_handler([&](auto& endpoint, std::string_view data) {
        printf("从[%s:%d]收到UDP数据:%.*s\n", 
            endpoint.address().to_string().c_str(), endpoint.port(),
            (int)data.size(), data.data());
        // 回复数据
        server.send(endpoint, "收到数据:" + std::string(data));
    });

    // 启动UDP服务端(监听8081端口)
    server.start("0.0.0.0", 8081);

    while (asio2::main_run()) {}

    return 0;
}
3.2.2 UDP客户端(广播上报)
#include "asio2/asio2.hpp"
#include <unistd.h>

int main() {
    asio2::udp_client client;

    // 启动UDP客户端(无需连接,直接发送)
    client.start();

    // 广播数据(嵌入式设备状态广播)
    while (asio2::main_run()) {
        std::string broadcast_data = "device_id:001,temp:26.0";
        // 发送到局域网广播地址(按需替换)
        client.send("192.168.1.255", 8081, broadcast_data);
        sleep(1);
    }

    return 0;
}

3.3 串口通信(外设交互)

#include "asio2/asio2.hpp"

int main() {
    // 创建串口对象(嵌入式串口路径:/dev/ttyS0/ttyUSB0)
    asio2::serial_port serial;

    // 配置串口参数:波特率115200,8位数据位,无校验,1位停止位
    serial.set_option(asio2::serial_port::baud_rate(115200));
    serial.set_option(asio2::serial_port::character_size(8));
    serial.set_option(asio2::serial_port::parity(asio2::serial_port::parity::none));
    serial.set_option(asio2::serial_port::stop_bits(asio2::serial_port::stop_bits::one));

    // 接收串口数据回调
    serial.set_recv_handler([&](std::string_view data) {
        printf("串口收到:%.*s\n", (int)data.size(), data.data());
    });

    // 打开串口(嵌入式需确保串口权限:chmod 777 /dev/ttyS0)
    serial.start("/dev/ttyS0");

    // 发送数据到串口(控制外设)
    while (asio2::main_run()) {
        serial.send("read_sensor\r\n");  // 发送指令读取传感器
        sleep(2);
    }

    return 0;
}

四、高级特性(嵌入式优化)

4.1 异步日志集成(结合spdlog)

#include "asio2/asio2.hpp"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"

int main() {
    // 初始化spdlog(嵌入式滚动日志)
    auto logger = spdlog::rotating_logger_mt("asio2_log", "/tmp/asio2.log", 10*1024*1024, 3);
    spdlog::set_default_logger(logger);

    asio2::tcp_server server;

    // 连接日志
    server.set_accept_handler([&](auto& session) {
        spdlog::info("客户端[{}]连接", session.remote_address());
    });

    // 数据接收日志
    server.set_recv_handler([&](auto& session, std::string_view data) {
        spdlog::debug("收到[{}]数据:{}", session.remote_address(), std::string(data));
    });

    // 断开日志
    server.set_disconnect_handler([&](auto& session) {
        spdlog::warn("客户端[{}]断开", session.remote_address());
    });

    server.start("0.0.0.0", 8080);

    while (asio2::main_run()) {}

    return 0;
}

4.2 嵌入式资源优化

#include "asio2/asio2.hpp"

int main() {
    asio2::tcp_client client;

    // 1. 限制IO线程数(嵌入式建议1-2线程)
    client.io_context().set_threads(1);

    // 2. 关闭Nagle算法(降低TCP延迟,嵌入式实时通信必备)
    client.set_option(asio2::socket_base::tcp_no_delay(true));

    // 3. 设置接收缓冲区大小(适配嵌入式小内存)
    client.set_option(asio2::socket_base::receive_buffer_size(4096));

    // 4. 重连间隔优化(避免频繁重连占用CPU)
    client.set_reconnect_interval(std::chrono::seconds(3));

    client.start("192.168.1.200", 8080, asio2::reconnect::enable);

    while (asio2::main_run()) {}

    return 0;
}

4.3 断线重连高级配置

#include "asio2/asio2.hpp"

int main() {
    asio2::tcp_client client;

    // 自定义重连逻辑:最多重连10次,间隔递增
    int reconnect_count = 0;
    const int max_reconnect = 10;
    client.set_reconnect_handler([&]() {
        reconnect_count++;
        if (reconnect_count > max_reconnect) {
            printf("重连%d次失败,停止重连\n", max_reconnect);
            return false; // 停止重连
        }
        // 重连间隔:1s,2s,4s...(指数退避)
        int interval = 1 << (reconnect_count - 1);
        printf("第%d次重连,间隔%d秒\n", reconnect_count, interval);
        client.set_reconnect_interval(std::chrono::seconds(interval));
        return true;
    });

    client.start("192.168.1.200", 8080, asio2::reconnect::enable);

    while (asio2::main_run()) {}

    return 0;
}

五、避坑指南/常见问题

5.1 交叉编译报错:asio未定义

错误信息
error: ‘asio’ has not been declared
解决方案
  • 确认已拷贝独立asio头文件到项目include目录;
  • CMake中添加-DASIO_STANDALONE宏(禁用boost依赖):
    add_compile_definitions(ASIO_STANDALONE)
    

5.2 串口打开失败

现象
failed to open serial port: Permission denied
解决方案
# 嵌入式设备修改串口权限
chmod 777 /dev/ttyS0
# 或添加用户到dialout组(永久生效)
usermod -aG dialout root

5.3 TCP客户端发送数据无响应

排查步骤
  1. 检查嵌入式设备与服务端的网络连通性:ping 192.168.1.200;
  2. 确认服务端端口已开放:telnet 192.168.1.200 8080;
  3. 关闭嵌入式防火墙(如iptables):iptables -F;
  4. 检查客户端是否开启重连:client.start(..., asio2::reconnect::enable)。

5.4 内存泄漏

原因

未释放session会话、IO上下文未正确停止。

解决方案
// 程序退出前手动停止IO上下文
void exit_handler() {
    client.stop();
    server.stop();
    asio2::main_stop();
}

int main() {
    // 注册退出信号处理(嵌入式捕获SIGINT/SIGTERM)
    signal(SIGINT, [](int) { exit_handler(); });
    signal(SIGTERM, [](int) { exit_handler(); });

    // ... 业务代码 ...

    return 0;
}

5.5 中文数据乱码

解决方案
  • 通信双方统一使用UTF-8编码;
  • 发送/接收时指定编码格式:
    // 发送UTF-8中文
    std::string data = u8"嵌入式设备状态:正常";
    client.send(data);
    

六、附录

6.1 常用API速查表

API功能描述
asio2::tcp_server/udp_server创建TCP/UDP服务端
asio2::tcp_client/udp_client创建TCP/UDP客户端
server.start(ip, port)启动服务端监听
client.start(ip, port)启动客户端连接
set_accept_handler(cb)注册客户端连接回调
set_recv_handler(cb)注册数据接收回调
set_disconnect_handler(cb)注册断开连接回调
session.send(data)发送数据给客户端/服务端
is_connected()判断客户端是否连接成功
io_context().set_threads(n)设置IO线程数

6.2 参考链接

  • ASIO2官方文档:https://github.com/zhllxt/asio2
  • ASIO2嵌入式示例:https://github.com/zhllxt/asio2/tree/master/example/serial_port
  • 独立ASIO文档:https://think-async.com/Asio/

6.3 术语解释

  • IO上下文(io_context):asio核心事件循环,管理网络/串口IO事件;
  • 会话(session):单个客户端与服务端的通信实例;
  • 异步非阻塞:IO操作不阻塞主线程,通过回调处理结果;
  • Nagle算法:TCP合并小数据包发送,关闭可降低延迟;
  • 指数退避:重连间隔按指数增长,避免频繁重试占用资源。

更多推荐