網站首頁 編程語言 正文
一、網絡編程
盡管 Boost.Asio 可以異步處理任何類型的數據,但它主要用于網絡編程。這是因為 Boost.Asio 早在添加額外的 I/O 對象之前就支持網絡功能。網絡函數非常適合異步操作,因為通過網絡傳輸數據可能需要很長時間,這意味著確認和錯誤可能不會像發送或接收數據的函數那樣快。
二、庫示例
Boost.Asio 提供了許多 I/O 對象來開發網絡程序。示例 32.5 使用類 boost::asio::ip::tcp::socket 與另一臺計算機建立連接。此示例向網絡服務器發送 HTTP 請求以下載主頁。
示例 32.5。帶有 boost::asio::ip::tcp::socket 的網絡客戶端
#include <boost/asio/io_service.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <array>
#include <string>
#include <iostream>
using namespace boost::asio;
using namespace boost::asio::ip;
io_service ioservice;
tcp::resolver resolv{ioservice};
tcp::socket tcp_socket{ioservice};
std::array<char, 4096> bytes;
void read_handler(const boost::system::error_code &ec,
std::size_t bytes_transferred)
{
if (!ec)
{
std::cout.write(bytes.data(), bytes_transferred);
tcp_socket.async_read_some(buffer(bytes), read_handler);
}
}
void connect_handler(const boost::system::error_code &ec)
{
if (!ec)
{
std::string r =
"GET / HTTP/1.1\r\nHost: theboostcpplibraries.com\r\n\r\n";
write(tcp_socket, buffer(r));
tcp_socket.async_read_some(buffer(bytes), read_handler);
}
}
void resolve_handler(const boost::system::error_code &ec,
tcp::resolver::iterator it)
{
if (!ec)
tcp_socket.async_connect(*it, connect_handler);
}
int main()
{
tcp::resolver::query q{"theboostcpplibraries.com", "80"};
resolv.async_resolve(q, resolve_handler);
ioservice.run();
}
Example32.5
示例 32.5 使用了三個處理程序:connect_handler() 和 read_handler() 在建立連接并接收到數據時被調用。 resolve_handler() 用于名稱解析。
因為只有在建立連接之后才能接收數據,并且因為只有在解析名稱之后才能建立連接,所以各種異步操作都是在處理程序中啟動的。在 resolve_handler() 中,指向從名稱解析的端點的迭代器 it 與 tcp_socket 一起用于建立連接。在 connect_handler() 中,訪問 tcp_socket 以發送 HTTP 請求并開始接收數據。由于所有操作都是異步的,處理程序被傳遞給各自的函數。根據操作,可能需要傳遞其他參數。例如,迭代器它指的是從名稱解析的端點。數組字節用于存儲接收到的數據。
在 main() 中,boost::asio::ip::tcp::resolver::query 被實例化以創建對象 q。 q 表示對名稱解析器的查詢,一個類型為 boost::asio::ip::tcp::resolver 的 I/O 對象。通過將 q 傳遞給 async_resolve(),啟動異步操作來解析名稱。示例 32.5 解析名稱 theboostcpplibraries.com。異步操作啟動后,在 I/O 服務對象上調用 run() 以將控制權傳遞給操作系統。
解析名稱后,將調用 resolve_handler()。處理程序首先檢查名稱解析是否成功。在這種情況下,ec 為 0。只有這樣才能訪問套接字以建立連接。要連接的服務器地址由第二個參數提供,其類型為 boost::asio::ip::tcp::resolver::iterator。該參數是名稱解析的結果。
對 async_connect() 的調用之后是對處理程序 connect_handler() 的調用。再次首先檢查 ec 以確定是否可以建立連接。如果是這樣,則在套接字上調用 async_read_some()。通過此調用,開始讀取數據。接收到的數據存儲在數組字節中,作為第一個參數傳遞給 async_read_some()。
當接收到一個或多個字節并將其復制到字節時調用 read_handler()。 std::size_t 類型的參數 bytes_transferred 包含已接收的字節數。像往常一樣,處理程序應該首先檢查異步操作是否成功完成。只有在這種情況下,才會將數據寫入標準輸出。
請注意,在將數據寫入 std::cout 后,read_handler() 會再次調用 async_read_some()。這是必需的,因為您無法確定整個主頁是否已在單個異步操作中下載并復制到字節中。對 async_read_some() 的重復調用和對 read_handler() 的重復調用僅在連接關閉時結束,這發生在網絡服務器發送整個主頁時。然后 read_handler() 在 ec 中報告錯誤。此時,不會向 std::cout 寫入更多數據,并且不會在套接字上調用 async_read()。因為沒有掛起的異步操作,程序退出。
示例 32.6。具有 boost::asio::ip::tcp::acceptor 的時間服務器
#include <boost/asio/io_service.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <string>
#include <ctime>
using namespace boost::asio;
using namespace boost::asio::ip;
io_service ioservice;
tcp::endpoint tcp_endpoint{tcp::v4(), 2014};
tcp::acceptor tcp_acceptor{ioservice, tcp_endpoint};
tcp::socket tcp_socket{ioservice};
std::string data;
void write_handler(const boost::system::error_code &ec,
std::size_t bytes_transferred)
{
if (!ec)
tcp_socket.shutdown(tcp::socket::shutdown_send);
}
void accept_handler(const boost::system::error_code &ec)
{
if (!ec)
{
std::time_t now = std::time(nullptr);
data = std::ctime(&now);
async_write(tcp_socket, buffer(data), write_handler);
}
}
int main()
{
tcp_acceptor.listen();
tcp_acceptor.async_accept(tcp_socket, accept_handler);
ioservice.run();
}
Example32.6
示例 32.6 是一個時間服務器。您可以連接 telnet 客戶端以獲取當前時間。之后時間服務器關閉。
時間服務器使用 I/O 對象 boost::asio::ip::tcp::acceptor 來接受來自另一個程序的傳入連接。您必須初始化對象,以便它知道在哪個端口上使用哪個協議。在示例中,boost::asio::ip::tcp::endpoint 類型的變量 tcp_endpoint 用于告訴 tcp_acceptor 在端口 2014 上接受 Internet 協議版本 4 的傳入連接。
接收器初始化后,調用listen() 使接收器開始監聽。然后調用 async_accept() 以接受第一次連接嘗試。必須將套接字作為第一個參數傳遞給 async_accept(),該參數將用于在新連接上發送和接收數據。
一旦另一個程序建立連接,就會調用accept_handler()。如果連接建立成功,當前時間會通過 boost::asio::async_write() 發送。此函數將 data 中的所有數據寫入套接字。 boost::asio::ip::tcp::socket 還提供了成員函數 async_write_some()。此函數在至少發送一個字節時調用處理程序。然后處理程序必須檢查發送了多少字節以及還需要發送多少字節。然后,它必須再次調用 async_write_some()。使用 boost::asio::async_write() 可以避免重復計算要發送的字節數和調用 async_write_some()。使用此函數開始的異步操作僅在數據中的所有字節都發送完畢后才完成。
發送數據后,會調用 write_handler()。該函數使用參數 boost::asio::ip::tcp::socket::shutdown_send 調用shutdown(),表示程序已完成通過套接字發送數據。由于沒有待處理的異步操作,示例 32.6 退出。請注意,雖然 data 僅在 accept_handler() 中使用,但它不能是局部變量。數據通過 boost::asio::buffer() 引用傳遞到 boost::asio::async_write()。當 boost::asio::async_write() 和 accept_handler() 返回時,異步操作已開始,但尚未完成。數據必須存在,直到異步操作完成。如果數據是一個全局變量,這是有保證的。
練習
開發可以將文件從一臺計算機傳輸到另一臺計算機的客戶端和服務器。當服務器啟動時,它應該顯示所有本地接口的 IP 地址列表并等待客戶端連接。當客戶端啟動時,來自服務器的 IP 地址和本地文件的名稱應作為命令行選項傳遞。客戶端應將文件傳輸到服務器,服務器將其保存到當前工作目錄。在傳輸期間,客戶端應該顯示某種進度指示器,以便用戶知道傳輸正在進行中。使用回調實現客戶端和服務器。
原文鏈接:https://yamagota.blog.csdn.net/article/details/127596214
相關推薦
- 2023-04-01 C++11中的智能指針和垃圾回收使用_C 語言
- 2022-10-12 C語言實現面向對象的方法詳解_C 語言
- 2022-04-23 C#面向對象的23種設計模式介紹_C#教程
- 2022-11-20 Python實現Tracert追蹤TTL值的方法詳解_python
- 2022-07-18 Uniapp中調整web-view的高度、獲取當前的web-view頁面URL
- 2023-10-11 Mybatis-Plus條件構造器的select
- 2021-11-06 Linux?遠程管理及sshd服務驗證知識點詳解_Linux
- 2022-06-21 C++簡明分析臨時對象是什么_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支