Boost Asio TCP异步服务端和客户端

服务端
消息分两次发送,第一次发送head,第二次发送body。接收也是先接收head,然后通过head结构中的body长度字段再接收body。
TcpServer.h

#pragma once
#include <atomic>
#include <vector>
#include <unordered_set>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/system/system_error.hpp>
#include "Connection.h"using namespace boost::asio;
using namespace boost::asio::ip;
using namespace boost::system;class TcpServer : public Connection::Listener {
public:using Handler = std::function<void(std::vector<uint8_t>, MessageType)>;TcpServer(uint16_t port, Handler&& handler);~TcpServer();void _startListen();void _startAccept();void _handleAccept(const error_code& error, std::shared_ptr<tcp::socket> socket);virtual void onDataReceived(ConnectionPtr connection, const std::vector<uint8_t>& data, MessageType type);void send(const char*, int size);private:uint16_t m_localPort;io_service m_oAcceptService;io_service::work m_oAcceptWork;tcp::acceptor *m_pAcceptor = nullptr;std::atomic_bool m_bStop = false;mutable boost::shared_mutex _connectionMutex;std::unordered_set<ConnectionPtr> _connections;Handler m_handler;
};

TcpServer.cpp

#include "TcpServer.h"
#include <boost/asio/buffer.hpp>
#include <fstream>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/asio.hpp>TcpServer::TcpServer(uint16_t port, Handler&& handler): m_oAcceptWork(m_oAcceptService), m_localPort(port), m_handler(handler)
{m_pAcceptor = new boost::asio::ip::tcp::acceptor(m_oAcceptService);_startListen();_startAccept();std::thread([&]() {while (1){m_oAcceptService.run();}}).detach();
}TcpServer::~TcpServer() {m_bStop = true;
}void TcpServer::_startListen() {boost::asio::ip::tcp::endpoint localEndpoint(boost::asio::ip::tcp::v4(), m_localPort);m_pAcceptor->open(localEndpoint.protocol());m_pAcceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));boost::asio::ip::tcp::no_delay noDelayOption(false);m_pAcceptor->set_option(noDelayOption);boost::system::error_code ec;boost::system::error_code code = m_pAcceptor->bind(localEndpoint, ec);if (!ec.value()){m_pAcceptor->listen();}elsestd::cout << (std::string("TcpServer start listen exception: ") + ec.message().c_str()) << std::endl;}void TcpServer::_startAccept() {if (m_bStop){return;}auto socket = std::make_shared<boost::asio::ip::tcp::socket>(m_oAcceptService);m_pAcceptor->async_accept(*socket, boost::bind(&TcpServer::_handleAccept, this, boost::asio::placeholders::error, socket));}void TcpServer::_handleAccept(const error_code& error, std::shared_ptr<tcp::socket> socket) {if (!error) {// read and write.std::cout << "_handleAccept" << std::endl;auto connection = std::make_shared<Connection>(std::move(*socket), socket->get_io_service(), this);boost::unique_lock<boost::shared_mutex> lock(_connectionMutex);_connections.emplace(connection);lock.unlock();connection->start();}_startAccept();}void TcpServer::onDataReceived(ConnectionPtr connection, const std::vector<uint8_t>& data, MessageType type) {//connection->send(data);m_handler(data, type);
}void TcpServer::send(const char* data, int size)
{for (auto conn : _connections){conn->send(data, size);}
}

Connection.h

#pragma once
#define BOOST_ASIO_DISABLE_STD_CHRONO
#include <boost/asio.hpp>
#include <boost/date_time/time_duration.hpp>
#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/mutex.hpp>
#include <atomic>
#include <memory>
#include <list>
#include <future>
#include <boost/asio/steady_timer.hpp>
#include "message.h"namespace pt = boost::posix_time;
namespace placeholders = boost::asio::placeholders;
using boost::asio::buffer;
using boost::asio::const_buffer;// Connection的最大作用是保存TcpServer连接的客户端socket,以及单独启动线程或异步进行数据收发
class Connection : public std::enable_shared_from_this<Connection> {
public:class Listener;Connection(boost::asio::ip::tcp::socket&& socket, boost::asio::io_service& oTimerService, Listener* listener);~Connection();void start();void stop();void _ranDataReception();void _handleReadHeader(const boost::system::error_code& error);void _handleReadData(const boost::system::error_code& error, const std::vector<uint8_t>& body, MessageType type);void send(const char* data, int size);void send(const std::vector<uint8_t>& data);void on_write(const boost::system::error_code & err, size_t bytes);private:bool _stopped = false;boost::asio::ip::tcp::socket _socket;MessageHeader _header;Listener* _listener;
};typedef std::shared_ptr<Connection> ConnectionPtr;class Connection::Listener {
public:virtual ~Listener() {}virtual void onDataReceived(ConnectionPtr connection, const std::vector<uint8_t>& data, MessageType type) = 0;
};

Connection.cpp

#include "Connection.h"
#include <boost/bind.hpp>
#include <functional>
#include <iostream>Connection::Connection(boost::asio::ip::tcp::socket&& socket, boost::asio::io_service& oTimerService, Listener* listener): _socket(std::move(socket)), _listener(listener)
{
}Connection::~Connection()
{}void Connection::start()
{_stopped = false;_ranDataReception();
}void Connection::stop()
{_stopped = true;
}void Connection::_ranDataReception() {if (!_stopped){memset(&_header, 0, sizeof(MessageHeader));boost::system::error_code oError;boost::asio::async_read(_socket, boost::asio::buffer(&_header, sizeof(MessageHeader)),boost::asio::transfer_exactly(sizeof(MessageHeader)),boost::bind(&Connection::_handleReadHeader, shared_from_this(), oError));}
}void Connection::_handleReadHeader(const boost::system::error_code& error) {if (!_stopped) {if (!error) {MessageType type = _header.type;int bodyLen = _header.length;//std::string strBody;std::vector<uint8_t> strBody;strBody.resize(bodyLen);//boost::system::error_code error;size_t iReadSize = _socket.read_some(boost::asio::buffer(strBody.data(), bodyLen), error);while (!error){if (iReadSize < bodyLen){iReadSize += _socket.read_some(boost::asio::buffer(strBody.data() + iReadSize, bodyLen - iReadSize), error);}else{break;}}if (!error && iReadSize == bodyLen){_handleReadData(error, strBody, type);}else{}}}
}void Connection::_handleReadData(const boost::system::error_code& error, const std::vector<uint8_t>& body, MessageType type)
{//if (!_stopped){if (!error){_listener->onDataReceived(shared_from_this(), body, type);_ranDataReception();}}
}void Connection::send(const char* data, int size)
{boost::system::error_code error;_socket.async_write_some(boost::asio::buffer(data, size),boost::bind(&Connection::on_write, this,boost::placeholders::_1,boost::placeholders::_2));
}void Connection::send(const std::vector<uint8_t>& data)
{boost::system::error_code error;_socket.async_write_some(boost::asio::buffer(data.data(), data.size()), boost::bind(&Connection::on_write, this, boost::placeholders::_1, boost::placeholders::_2));
}void Connection::on_write(const boost::system::error_code & err, size_t bytes)
{}

客户端
Network.h

#pragma once
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>namespace sinftech {
namespace tv {
class Network {
public:Network(boost::asio::io_service& ioService, const std::string& address, uint16_t port);~Network();void start();void stop();size_t send(char* data, size_t size);size_t receive(char* data, size_t size);private:bool _running;boost::asio::ip::tcp::socket _socket;boost::asio::ip::tcp::endpoint _remoteEndpoint;
};
}//namespace tv
}//namespace sinftech

Network.cpp (windows平台setopt设置超时时间使用整数,Linux平台使用结构体struct timeval)

#include "Network.h"
#include <boost/asio/buffer.hpp>
#include <thread>namespace sinftech {
namespace tv {Network::Network(boost::asio::io_service& ioService, const std::string& address, uint16_t port): _running(false), _socket(ioService), _remoteEndpoint(boost::asio::ip::address::from_string(address), port) 
{int timeout = 1000;int iRet = setsockopt(_socket.native(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));if (0 != iRet){printf("Set rcv time out error\n");}int iRcvSize = 1024 * 1000;iRet = setsockopt(_socket.native(), SOL_SOCKET, SO_RCVBUF, (char *)&iRcvSize, sizeof(iRcvSize));if (0 != iRet){printf("Set rcv buffer size error\n");}start();
}Network::~Network() {stop();
}void Network::start() {_running = true;
}void Network::stop() {_running = false;boost::system::error_code ec;_socket.close(ec);
}size_t Network::send(char* data, size_t size) {size_t bytesSent = 0;if (_running) {boost::system::error_code ec;if (!_socket.is_open()) {_socket.connect(_remoteEndpoint, ec);}if (!ec) {            bytesSent = _socket.write_some(boost::asio::buffer(data, size), ec);}if (ec) {_socket.close(ec);}}return bytesSent;
}size_t Network::receive(char* data, size_t size) {size_t bytesRecv = 0;if (_running) {boost::system::error_code ec;if (!_socket.is_open()) {_socket.connect(_remoteEndpoint, ec);}if (!ec) {            bytesRecv = _socket.read_some(boost::asio::buffer(data, size), ec);}if (ec) {_socket.close(ec);}}return bytesRecv;
}}//namespace tv
}//namespace sinftech

注意,Linux和Windows平台使用setopt设置超时参数的方式是不同的。在Linux上,你可以使用setsockopt来设置套接字选项,包括读取和写入超时。具体的选项是SO_RCVTIMEO和SO_SNDTIMEO。

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>int set_socket_timeout(int sockfd, int timeout_ms) {struct timeval timeout;timeout.tv_sec = timeout_ms / 1000;timeout.tv_usec = (timeout_ms % 1000) * 1000;// 设置接收超时if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {perror("setsockopt failed");return -1;}// 设置发送超时if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {perror("setsockopt failed");return -1;}return 0;
}

在Windows上,setsockopt同样用于设置套接字选项,但超时时间是以毫秒为单位的整数,而不是timeval结构体。你需要使用SO_RCVTIMEO和SO_SNDTIMEO选项,并传递一个DWORD类型的值。

#include <winsock2.h>
#include <ws2tcpip.h>#pragma comment(lib, "Ws2_32.lib")int set_socket_timeout(SOCKET sockfd, DWORD timeout_ms) {// 设置接收超时if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_ms, sizeof(timeout_ms)) == SOCKET_ERROR) {printf("setsockopt failed with error: %ld\n", WSAGetLastError());return -1;}// 设置发送超时if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout_ms, sizeof(timeout_ms)) == SOCKET_ERROR) {printf("setsockopt failed with error: %ld\n", WSAGetLastError());return -1;}return 0;
}// 在程序开始时需要初始化Winsock库
int main() {WSADATA wsaData;int result = WSAStartup(MAKEWORD(2, 2), &wsaData);if (result != 0) {printf("WSAStartup failed: %d\n", result);return 1;}// ... 创建并配置套接字 ...// 在程序结束前清理Winsock库WSACleanup();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/2184.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于光偏振与光学调制实现白光干涉相移

基于光的偏振特性和一些光学元件对光的调制作用&#xff0c;实现白光干涉中的光学相移原理是一个复杂而精细的过程。以下是对这一原理的详细解释&#xff1a; 一、光的偏振特性 光的偏振是指光波在传播过程中&#xff0c;光矢量的方向和大小有规则变化的现象。圆偏振光的电场…

Flutter:封装ActionSheet 操作菜单

演示效果图 action_sheet_util.dart import package:ducafe_ui_core/ducafe_ui_core.dart; import package:flutter/material.dart; import package:demo/common/index.dart;class ActionSheetUtil {/// 底部操作表/// [context] 上下文/// [title] 标题/// [items] 选项列表 …

使用yarn命令创建Vue3项目

文章目录 1.技术栈2.创建流程2.1创建vue3项目2.2选择配置项2.3进入项目目录 3.使用Yarn启动项目3.1安装依赖3.2运行项目 1.技术栈 yarnvitevue3 2.创建流程 2.1创建vue3项目 vue create 项目名称2.2选择配置项 直接回车可选择Vue3 2.3进入项目目录 cd 项目名称默认在当前…

【Node.js的安装与配置】

目录&#xff1a; 一&#xff1a;下载Node.js二&#xff1a;安装Node.js三&#xff1a;配置存放目录四&#xff1a;配置环境变量五&#xff1a;配置淘宝镜像六&#xff1a;测试Node.js 一&#xff1a;下载Node.js &#x1f534; 下载地址&#xff1a;https://www.nodejs.com.cn…

【AIGC】SYNCAMMASTER:多视角多像机的视频生成

标题&#xff1a;SYNCAMMASTER: SYNCHRONIZING MULTI-CAMERA VIDEO GENERATION FROM DIVERSE VIEWPOINTS 主页&#xff1a;https://jianhongbai.github.io/SynCamMaster/ 代码&#xff1a;https://github.com/KwaiVGI/SynCamMaster 文章目录 摘要一、引言二、使用步骤2.1 TextT…

左神算法基础提升--1

文章目录 哈希函数哈希函数的主要特点确定性快速计算输出长度固定离散性 哈希表哈希表的原理解题 布隆过滤器布隆过滤器的主要特点高效性快速查询空间效率误报率 布隆过滤器的原理 一致性哈希一致性哈希原理一致性哈希应用 哈希函数 哈希函数是一种将任意长度的输入&#xff0…

【Go】Go Gin框架初识(一)

1. 什么是Gin框架 Gin框架&#xff1a;是一个由 Golang 语言开发的 web 框架&#xff0c;能够极大提高开发 web 应用的效率&#xff01; 1.1 什么是web框架 web框架体系图&#xff08;前后端不分离&#xff09;如下图所示&#xff1a; 从上图中我们可以发现一个Web框架最重要…

VS Code 的扩展下载安装的最新方式

离线包的下载 在 2024年的时候&#xff0c;在VS Code的官方扩展市场&#xff1a;https://marketplace.visualstudio.com/ &#xff0c; 搜索到需要的扩展之后&#xff0c;是可以在对应的页面现在最新版本和几个历史版本的扩展的安装包。 下载下来的扩展包的文件是后缀是 vsix …

【Vue3 入门到实战】3. ref 和 reactive区别和适用场景

目录 ​编辑 1. ref 部分 1.1 ref定义基本数据类型 1.2 ref 定义引用数据类型 2. reactive 函数 3. ref 和 reactive 对比 3.1 原理 3.2 区别 3.3 使用原则 在 Vue 3 中 ref 和 reactive 是用于创建响应式数据的两个核心函数。它们都属于 Composition API 的一部分&…

浅谈云计算07 | 云安全机制

云计算安全机制 一、引言二、加密技术&#xff1a;数据的隐形护盾三、散列机制&#xff1a;数据完整性的忠诚卫士四、数字签名&#xff1a;数据来源与真伪的鉴定专家五、公钥基础设施&#xff08;PKI&#xff09;&#xff1a;信任的基石六、身份与访问管理&#xff08;IAM&…

【Sql递归查询】Mysql、Oracle、SQL Server、PostgreSQL 实现递归查询的区别与案例(详解)

文章目录 Mysql 5.7 递归查询Mysql 8 实现递归查询Oracle递归示例SQL Server 递归查询示例PostgreSQL 递归查询示例 更多相关内容可查看 Mysql 5.7 递归查询 MySQL 5.7 本身不直接支持标准 SQL 中的递归查询语法&#xff08;如 WITH RECURSIVE 这种常见的递归查询方式&#xf…

【Unity3D】【已解决】TextMeshPro无法显示中文的解决方法

TextMeshPro无法显示中文的解决方法 现象解决方法Assets 目录中新建一个字体文件夹在C:\Windows\Fonts 中随便找一个中文字体的字体文件把字体文件拖到第一步创建的文件夹中右键导入的字体&#xff0c;Create---TextMeshPro---Font Asset&#xff0c;创建字体文件资源把 SDF文件…

ShaderJoy —— 如何判别直线是否和二次贝塞尔曲线相交【GLSL】

效果图 关键代码解析 bool IntersectsQuadraticBezier (vec2 src, vec2 dest) {float A = (CONTROL_POINT_A - 2.0 * CONTROL_POINT_B

第十二章:算法与程序设计

文章目录&#xff1a; 一&#xff1a;基本概念 1.算法与程序 1.1 算法 1.2 程序 2.编译预处理 3.面向对象技术 4.程序设计方法 5.SOP标志作业流程 6.工具 6.1 自然语言 6.2 流程图 6.3 N/S图 6.4 伪代码 6.5 计算机语言 二&#xff1a;程序设计 基础 1.常数 …

【BLE】CC2541之ADC

本文最后修改时间&#xff1a;2022年04月12日 23:00 一、本节简介 本文介绍如何通过P05口采集电压值。 二、实验平台 1&#xff09;CC2541平台 ①协议栈版本&#xff1a;BLE-CC254x-1.4.0 ②编译软件&#xff1a;IAR 10.20.1 ③硬件平台&#xff1a;香瓜CC2541开发板、USB…

SpeingMVC框架(三)

目录 五、响应数据与结果视图 1、返回值分类 2、springmvc的请求转发和重定向 六、异常处理 1、处理思路 2、自定义异常处理器 七、springmvc中的拦截器 1、拦截器概述 2、自定义拦截器步骤 五、响应数据与结果视图 1、返回值分类 返回String&#xff1a;Controller方…

Hadoop3.x 万字解析,从入门到剖析源码

&#x1f496; 欢迎来到我的博客&#xff01; 非常高兴能在这里与您相遇。在这里&#xff0c;您不仅能获得有趣的技术分享&#xff0c;还能感受到轻松愉快的氛围。无论您是编程新手&#xff0c;还是资深开发者&#xff0c;都能在这里找到属于您的知识宝藏&#xff0c;学习和成长…

【Vue】分享一个快速入门的前端框架以及如何搭建

先上效果图: 登录 菜单: 下载地址: 链接&#xff1a;https://pan.baidu.com/s/1m-ZlBARWU6_2n8jZil_RAQ 提取码&#xff1a;ui20 … 主要是可以自定义设置token,更改后端请求地址较为方便。 应用设置: 登录与token设置: 在这里设置不用登录,可以请求的接口: request.js i…

汽车免拆诊断案例 | 2007 款法拉利 599 GTB 车发动机故障灯异常点亮

故障现象  一辆2007款法拉利599 GTB车&#xff0c;搭载6.0 L V12自然吸气发动机&#xff08;图1&#xff09;&#xff0c;累计行驶里程约为6万km。该车因发动机故障灯异常点亮进厂检修。 图1 发动机的布置 故障诊断 接车后试车&#xff0c;发动机怠速轻微抖动&#xff0c;…

Emacs 折腾日记(九)——elisp 数组与序列

elisp 中序列是数组和列表的统称&#xff0c;序列的共性是内部数据有一个先后的顺序&#xff0c;它与C/C 中有序列表类似。 elisp 中的数组包括向量、字符串、char-table 和布尔向量&#xff0c;它们的关系如下: 在之前一章中已经介绍了序列中的一种类型——列表&#xff0c…