C++ 網絡編程(15) 利用asio協程搭建異步服務器

🚀 [協程與異步服務器實戰]:[C++20協程原理與Boost.Asio異步服務器開發]
📅 更新時間:2025年07月05日
🏷? 標簽:C++20 | 協程 | Boost.Asio | 異步編程 | 網絡服務器

文章目錄

  • 前言
  • 一、什么是協程?
  • 二、線程與協程?
  • 三、使用協程搭建異步服務器進行通信
    • 1.服務端
      • 1.流程圖
      • 2.服務端代碼
        • 1.主函數入口
        • 2.lisenter 協程函數
        • 3.echo協程函數
    • 2.客戶端
  • 四、測試
  • 總結


前言

今天我們學習協程的基本概念,以及如何用協程來搭建一個簡單的異步服務器來進行與客戶端的收發數據


一、什么是協程?

協程(Coroutine)是一種比線程更輕量級的“并發編程”方式
它允許你在一個線程內,把任務分成多個可以掛起和恢復的小段,寫出“像同步一樣的異步代碼”。

協程的特點
可以在執行過程中主動暫停(掛起),等條件滿足后恢復執行
多個協程可以在同一個線程內切換,切換速度非常快
由程序員或框架調度,而不是操作系統

總結
協程就是可以隨時掛起和恢復的輕量級任務,讓你用很少的資源實現高效的并發和異步編程

二、線程與協程?

直觀比喻
線程像“多個人各自做事”
協程像“一個人做多件事,可以隨時暫停當前任務,去做別的,再回來繼續”

三、使用協程搭建異步服務器進行通信

1.服務端

1.流程圖

在這里插入圖片描述

1.程序啟動(main)
初始化 io_context
設置信號處理(監聽 Ctrl+C 等信號,優雅退出)
啟動 lisenter 協程

2.lisenter 協程
創建 acceptor,監聽 10086 端口
進入無限循環:
異步等待新連接(co_await acceptor.async_accept()
每有一個新連接,啟動一個 echo 協程處理該連接

3.echo 協程
進入無限循環:
異步讀取客戶端數據(co_await socket.async_read_some()
異步寫回數據(co_await async_write()
信號處理
收到終止信號時,調用 ioc.stop(),優雅關閉服務器

2.服務端代碼

1.主函數入口

我們在主函數中利用 try catch 來進行跑代碼,這樣可以防止后續的出錯,
我們先定義一個信號集signal_set來實現服務器的優雅退出,當客戶端使用Ctrl+C等操作的時候,我們可以通知 上下文 io_context直接調用 .stop() 來暫停服務

int main()
{try{boost::asio::io_context ioc(1);boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);signals.async_wait([&](auto,auto){ioc.stop();});co_spawn(ioc,lisenter(),detached);//啟動協程ioc.run();}catch(std::exception& e){std::cout << "main Exception is" << e.what() << std::endl;}
}

在寫 Lambda 表達式的時候如果要調用上下文io_context必須用&捕獲
核心原因是:io_context(以及很多 Asio 相關對象)本身禁止拷貝,只能被引用捕獲,不能被值捕獲

co_spawn(ioc,lisenter(),detached);//啟動協程

co_spawn
co_spawn表示啟動一個協程,參數分別為調度器執行的函數,以及啟動方式, 比如我們啟動了一個協程,deatched表示將協程對象分離出來,這種啟動方式可以啟動多個協程,他們都是獨立的,如何調度取決于調度器,在用戶的感知上更像是線程調度的模式,類似于并發運行,其實底層都是串行的

此時需要傳入三個參數
第一個參數
boost::asio::io_context
所有異步事件和協程都要綁定到某個 io_context,它負責調度和執行

第二個參數
是你自定義的協程函數,返回類型通常是 awaitable<void>
這里我們自定義的函數是lisenter()

第三個參數
協程的完成方式
總共有三種,分別是
detached
作用:協程分離運行,主程序不關心協程的返回值和異常。
用法:適合“只管啟動,不關心后續”的場景(如服務器監聽、后臺任務)

use_awaitable
作用:讓協程的結果可以被 co_await 等待,用于協程之間的嵌套和組合。
用法:適合你想在另一個協程里等待這個協程的結果

最后一種是自定義的回調函數

所以我們這句話

co_spawn(ioc,lisenter(),detached);//啟動協程

的完整意思就是
在 ioc 這個事件循環中,啟動一個 lisenter 協程,讓它自己運行,主程序不關心它的結果

2.lisenter 協程函數

我們定義一個協程函數,然后在協程函數中我們創建一個監聽器acceptor進行綁定上下文tcp協議端口號

然后我們調用一個死循環,內部異步的進行監聽然后創建一個socket,然后我們根據這個socket再創建一個協程echo單獨管理此客戶端的通信

awaitable<void> lisenter()
{auto executor = co_await this_coro::executor;//co_await異步獲取調度器tcp::acceptor acceptor(executor, { tcp::v4(),10086 });for (;;){tcp::socket socket = co_await acceptor.async_accept(use_awaitable);co_spawn(executor, echo(std::move(socket)), detached);//為每一個連接單獨啟動 一個協程進行收發數據}
}

如果要寫協程函數,必須是這種類型

awaitable<T>

我們在協程中進行了對當前協程獲取調度器的實現

auto executor = co_await this_coro::executor;//co_await異步獲取調度器

Boost.Asio 中,executor 是一個“執行環境”,負責調度和管理異步操作的執行
常見的 executorio_context::executor_type,它和 io_context 綁定

因為我們在主函數中使用了

co_spawn(ioc, lisenter(), detached)

啟動協程時,協程會自動和 ioc 綁定
但在協程體內,如果你要創建新的異步對象(如 acceptor),需要明確告訴它用哪個 executor,否則它不知道該和哪個事件循環關聯
所以相當于給這個監聽器綁定了一個上下文io_context

然后我們再來介紹一下
co_await
co_awaitC++20 協程的通用關鍵字,它的作用是等待一個 “可等待對象” 完成,并獲取其結果

比如這里我們用來獲取當前協程的調度器和監聽器分配的socket

auto executor = co_await this_coro::executor;
tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
3.echo協程函數

這個協程函數就是單獨為當前分配的客戶端進行讀寫通信的實現

awaitable<void>echo(tcp::socket socket)
{try{char data[1024];for (;;){std::size_t n=co_await socket.async_read_some(boost::asio::buffer(data), use_awaitable);co_await async_write(socket, boost::asio::buffer(data, n), use_awaitable);}}catch (std::exception& e){std::cout << "echo Exception is" << e.what() << std::endl;}
}

我們多次利用這個co_await實現了將看似同步的代碼,實現了異步等待的操作,比如這句

//獲取收到數據長度
std::size_t n=co_await 
socket.async_read_some(boost::asio::buffer(data), use_awaitable);//異步寫
co_await async_write
(socket, boost::asio::buffer(data, n), use_awaitable);

2.客戶端

客戶端我們還是用以前的簡易的版本,發送 hello world 進行測試,不考慮其他的問題

#include <iostream>
#include<boost/asio.hpp>using namespace std;
using namespace boost::asio::ip;
const int MAX_LENGTH = 1024;int main()
{try{boost::asio::io_context ioc;tcp::endpoint remote_ep(boost::asio::ip::make_address("127.0.0.1"), 10086);tcp::socket sock(ioc);boost::system::error_code error = boost::asio::error::host_not_found;sock.connect(remote_ep, error);if (error){cout << "connect failed, code is" << error.value() <<"  error msg is "<<error.what() << endl;return 0;}cout << "Enter Message:" << endl;char request[MAX_LENGTH];cin.getline(request, MAX_LENGTH);size_t request_len = strlen(request);boost::asio::write(sock, boost::asio::buffer(request, request_len));char reply[MAX_LENGTH];size_t reply_len = boost::asio::read(sock, boost::asio::buffer(reply, request_len));cout << "reply is" << string(reply,reply_len) << endl;getchar();}catch (std::exception& e){std::cout << "main exception is " << e.what() << std::endl;}return 0;
}

四、測試

客戶端成功與服務器進行通信
在這里插入圖片描述

總結

學習了協程了相關概念,以及如何利用協程來搭建一個簡易的異步服務器的小demo

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/87772.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/87772.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/87772.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Java21】在spring boot中使用虛擬線程

文章目錄 0.環境說明1.原理解析2.spring boot的方案3.注意事項&#xff08;施工中&#xff0c;歡迎補充&#xff09; 前置知識 虛擬線程VT&#xff08;Virtual Thread&#xff09; 0.環境說明 用于驗證的版本&#xff1a; spring boot: 3.3.3jdk: OpenJDK 21.0.5 spring boot…

利器:NPM和YARN及其他

文章目錄**1. 安裝 Yarn&#xff08;推薦方法&#xff09;****2. 驗證安裝****3. 常見問題及解決方法****① 權限不足&#xff08;Error: EPERM&#xff09;****② 網絡問題&#xff08;連接超時或下載失敗&#xff09;****③ 環境變量未正確配置****4. 替代安裝方法&#xff0…

跨平臺直播美顏SDK集成實錄:Android/iOS如何適配貼紙功能

眾所周知&#xff0c;直播平臺與短視頻平臺的貼紙功能不僅是用戶表達個性的方式&#xff0c;更是平臺提高用戶粘性和互動轉化的法寶。 可問題來了&#xff1a;如何讓一個貼紙功能&#xff0c;在Android和iOS兩大平臺上表現一致、運行流暢、加載穩定&#xff1f;這背后&#xff…

JavaWeb(蒼穹外賣)--學習筆記04(前端:HTML,CSS,JavaScript)

前言 本片文章是學習B站黑馬程序員蒼穹外賣的學習筆記。因為最近期末周&#xff0c;一直在應付考試所以就學的很少&#xff0c;恰好視頻中在講Nginx反向代理和負載均衡&#xff08;寫著對前端的內容做一個復習&#xff09; 概述&#xff1a; 1.web前端主要由三部分組成&…

智能學號抽取系統 V5.4.3.2 —— Vue.js 實現的多功能課堂隨機抽簽工具

智能學號抽取系統 V5.4.3.2 —— Vue.js 實現的多功能課堂隨機抽簽工具 在教學或會議場景中&#xff0c;我們經常需要隨機抽取一個或多個學號/編號來決定發言者、答題者或者參與者。為了提高效率和公平性&#xff0c;我們可以使用一些智能化的小工具來實現這一過程。 今天介紹…

從0開始學習R語言--Day39--Spearman 秩相關

在非參數統計中&#xff0c;不看數據的實際數值&#xff0c;單純比較兩組變量的值的排名是通用的基本方法&#xff0c;但在客觀數據中&#xff0c;很多變量的關系都是非線性的&#xff0c;其他的方法不是對樣本數據的大小和線性有要求&#xff0c;就是只能對比數據的差異性&…

WSL - Linux 安裝 Anaconda3-2025.06-0 詳細教程 [WSL 分發版均適用]

一、檢查系統狀態 安裝前先確認 WSL - Linxu 已正常啟動&#xff08;比如 Ubuntu&#xff09;&#xff0c;網絡連接穩定&#xff0c;并且系統磁盤有足夠空間&#xff0c;一般建議預留至少 5GB 以上的可用空間&#xff0c;避免因空間不足導致安裝失敗。 二、下載安裝包 Anacond…

熱血三國建筑攻略表格

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>熱血三國建筑攻略表格</title><style>…

SpringBoot+MySQL醫院掛號系統源碼

概述 基于SpringBootMySQL開發的醫院掛號系統完整源碼&#xff0c;該系統功能完善&#xff0c;包含從患者掛號到醫生管理的全流程解決方案&#xff0c;采用主流技術棧開發&#xff0c;代碼規范易于二次開發。 主要內容 系統包含完整的前后臺功能模塊&#xff1a; ??前臺功…

Linux系統之MySQL數據庫基礎

目錄 一、概述 數據庫概念 數據庫的類型 關系型數據庫模型 關系數據庫相關概念 二、安裝 1、mariadb安裝 2、mysql安裝 3、啟動并開機自啟 4、本地連接&#xff08;本地登錄&#xff09; 三、mysqld數據庫配置與命令 yum安裝后生成的目錄 mysqld服務器的啟動腳本 …

MySQL--InnoDB存儲引擎--頁結構

目錄 一、頁的大小 二、頁的分類 三、頁頭和頁尾 3.1 頁頭--File Header 3.2 頁尾--File Trailer 3.3 LSN 四、數據行 五、頁中數據的查詢 六、事務和索引在頁中的記錄 一、頁的大小 前面介紹了每個數據頁默認大小為16KB&#xff0c;是操作系統“數據塊” 4KB 的整數倍…

卡車檢測數據集-700張圖片交通運輸管理 智能監控系統 道路安全監測

跌倒檢測數據集-4500張圖片&#x1f4e6; 已發布目標檢測數據集合集&#xff08;持續更新&#xff09;&#x1f69b; Deteccin de carpa 2 Computer Vision Project&#x1f4cc; 數據集概覽包含類別&#x1f3af; 應用場景&#x1f5bc; 數據樣本展示&#x1f527; 使用建議&a…

Python爬蟲實戰:研究pangu庫相關技術

1. 引言 1.1 研究背景與意義 在數字化信息傳播時代,中文文本排版質量直接影響信息傳達效果。規范的排版要求中文與西文、數字間保持合理空格間距,但人工處理不僅效率低,且易出現一致性問題。隨著互聯網中文內容爆發式增長,傳統人工排版已無法滿足需求。Python 作為高效的…

day48-考試系統項目集群部署

1. ?考試系統項目集群架構圖負載均衡說明7層負載通過nginx對http請求進行轉發&#xff08;uri,ua,類型&#xff09;4層負載對端口負載均衡&#xff08;后端&#xff09;2. &#x1f4dd;環境準備角色主機ip負載均衡lb01/lb02172.16.1.5/172.16.1.6前端web集群web01/web02172.1…

Redis+Caffeine雙層緩存策略對比與實踐指南

RedisCaffeine雙層緩存策略對比與實踐指南在高并發場景下&#xff0c;緩存是提升系統性能和并發處理能力的關鍵手段。常見的緩存方案包括遠程緩存&#xff08;如Redis&#xff09;和本地緩存&#xff08;如Caffeine&#xff09;。單層緩存各有優劣&#xff0c;結合兩者優勢的雙…

FastAPI+React19 ERP系統實戰 第02期

一、搭建環境 1.1 創建Python虛擬環境 切換Python版本: pyenv local 3.12創建虛擬環境: python -m venv venv激活虛擬環境: venv\Scripts\activate1.2 安裝FastAPI項目依賴 requirements.txt fastapi==0.109.1

百度AI文心大模型4.5系列開源模型評測,從安裝部署到應用體驗

2025年6月30日&#xff0c;百度突然宣布&#xff0c;將旗下最新的大語言模型文心大模型4.5&#xff08;ERNIE 4.5&#xff09;全系列開源&#xff0c;震動整個AI行業。百度在GitCode平臺上開源了文心大模型4.5系列&#xff0c;包括ERNIE-4.5-VL-424B-A47B-Base-PT等多個型號。此…

windows安裝maven環境

在maven官網下載安裝包 https://maven.apache.org/download.cgi 下載完成后安裝maven&#xff0c;一般下載編輯好的 創建個maven目錄解壓出來即可 配置環境變量 根據剛剛的安裝路徑&#xff0c;新建一個命名為MAVEN_HOME的系統變量 新建完成點開系統變量的Path項&#xff0c;…

MySQL(117)何進行數據庫安全加密?

數據庫安全加密是保護敏感數據免受未授權訪問的重要手段。以下是一個詳細深入的步驟&#xff0c;介紹如何進行數據庫安全加密&#xff0c;包括數據傳輸加密和數據存儲加密。 一. 數據傳輸加密 確保數據在傳輸過程中被加密&#xff0c;以防止中間人攻擊。我們以MySQL為例&#x…

工程化實踐——標準化Eslint、PrettierTS

前端工程化中的標準化工具&#xff08;如Prettier、ESLint、Husky等&#xff09;雖然大幅提升了開發效率和代碼質量&#xff0c;但在實際使用中也存在一些限制和挑戰。以下從工具特性、團隊協作、開發體驗等維度詳細分析常見限制&#xff0c;并以Prettier為核心舉例說明&#x…