C++網(wǎng)絡(luò)編程詳細(xì)講解
一、網(wǎng)絡(luò)編程
盡管 Boost.Asio 可以異步處理任何類型的數(shù)據(jù),但它主要用于網(wǎng)絡(luò)編程。這是因為 Boost.Asio 早在添加額外的 I/O 對象之前就支持網(wǎng)絡(luò)功能。網(wǎng)絡(luò)函數(shù)非常適合異步操作,因為通過網(wǎng)絡(luò)傳輸數(shù)據(jù)可能需要很長時間,這意味著確認(rèn)和錯誤可能不會像發(fā)送或接收數(shù)據(jù)的函數(shù)那樣快。
二、庫示例
Boost.Asio 提供了許多 I/O 對象來開發(fā)網(wǎng)絡(luò)程序。示例 32.5 使用類 boost::asio::ip::tcp::socket 與另一臺計算機(jī)建立連接。此示例向網(wǎng)絡(luò)服務(wù)器發(fā)送 HTTP 請求以下載主頁。
示例 32.5。帶有 boost::asio::ip::tcp::socket 的網(wǎng)絡(luò)客戶端
#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(); }
示例 32.5 使用了三個處理程序:connect_handler() 和 read_handler() 在建立連接并接收到數(shù)據(jù)時被調(diào)用。 resolve_handler() 用于名稱解析。
因為只有在建立連接之后才能接收數(shù)據(jù),并且因為只有在解析名稱之后才能建立連接,所以各種異步操作都是在處理程序中啟動的。在 resolve_handler() 中,指向從名稱解析的端點的迭代器 it 與 tcp_socket 一起用于建立連接。在 connect_handler() 中,訪問 tcp_socket 以發(fā)送 HTTP 請求并開始接收數(shù)據(jù)。由于所有操作都是異步的,處理程序被傳遞給各自的函數(shù)。根據(jù)操作,可能需要傳遞其他參數(shù)。例如,迭代器它指的是從名稱解析的端點。數(shù)組字節(jié)用于存儲接收到的數(shù)據(jù)。
在 main() 中,boost::asio::ip::tcp::resolver::query 被實例化以創(chuàng)建對象 q。 q 表示對名稱解析器的查詢,一個類型為 boost::asio::ip::tcp::resolver 的 I/O 對象。通過將 q 傳遞給 async_resolve(),啟動異步操作來解析名稱。示例 32.5 解析名稱 theboostcpplibraries.com。異步操作啟動后,在 I/O 服務(wù)對象上調(diào)用 run() 以將控制權(quán)傳遞給操作系統(tǒng)。
解析名稱后,將調(diào)用 resolve_handler()。處理程序首先檢查名稱解析是否成功。在這種情況下,ec 為 0。只有這樣才能訪問套接字以建立連接。要連接的服務(wù)器地址由第二個參數(shù)提供,其類型為 boost::asio::ip::tcp::resolver::iterator。該參數(shù)是名稱解析的結(jié)果。
對 async_connect() 的調(diào)用之后是對處理程序 connect_handler() 的調(diào)用。再次首先檢查 ec 以確定是否可以建立連接。如果是這樣,則在套接字上調(diào)用 async_read_some()。通過此調(diào)用,開始讀取數(shù)據(jù)。接收到的數(shù)據(jù)存儲在數(shù)組字節(jié)中,作為第一個參數(shù)傳遞給 async_read_some()。
當(dāng)接收到一個或多個字節(jié)并將其復(fù)制到字節(jié)時調(diào)用 read_handler()。 std::size_t 類型的參數(shù) bytes_transferred 包含已接收的字節(jié)數(shù)。像往常一樣,處理程序應(yīng)該首先檢查異步操作是否成功完成。只有在這種情況下,才會將數(shù)據(jù)寫入標(biāo)準(zhǔn)輸出。
請注意,在將數(shù)據(jù)寫入 std::cout 后,read_handler() 會再次調(diào)用 async_read_some()。這是必需的,因為您無法確定整個主頁是否已在單個異步操作中下載并復(fù)制到字節(jié)中。對 async_read_some() 的重復(fù)調(diào)用和對 read_handler() 的重復(fù)調(diào)用僅在連接關(guān)閉時結(jié)束,這發(fā)生在網(wǎng)絡(luò)服務(wù)器發(fā)送整個主頁時。然后 read_handler() 在 ec 中報告錯誤。此時,不會向 std::cout 寫入更多數(shù)據(jù),并且不會在套接字上調(diào)用 async_read()。因為沒有掛起的異步操作,程序退出。
示例 32.6。具有 boost::asio::ip::tcp::acceptor 的時間服務(wù)器
#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(); }
示例 32.6 是一個時間服務(wù)器。您可以連接 telnet 客戶端以獲取當(dāng)前時間。之后時間服務(wù)器關(guān)閉。
時間服務(wù)器使用 I/O 對象 boost::asio::ip::tcp::acceptor 來接受來自另一個程序的傳入連接。您必須初始化對象,以便它知道在哪個端口上使用哪個協(xié)議。在示例中,boost::asio::ip::tcp::endpoint 類型的變量 tcp_endpoint 用于告訴 tcp_acceptor 在端口 2014 上接受 Internet 協(xié)議版本 4 的傳入連接。
接收器初始化后,調(diào)用listen() 使接收器開始監(jiān)聽。然后調(diào)用 async_accept() 以接受第一次連接嘗試。必須將套接字作為第一個參數(shù)傳遞給 async_accept(),該參數(shù)將用于在新連接上發(fā)送和接收數(shù)據(jù)。
一旦另一個程序建立連接,就會調(diào)用accept_handler()。如果連接建立成功,當(dāng)前時間會通過 boost::asio::async_write() 發(fā)送。此函數(shù)將 data 中的所有數(shù)據(jù)寫入套接字。 boost::asio::ip::tcp::socket 還提供了成員函數(shù) async_write_some()。此函數(shù)在至少發(fā)送一個字節(jié)時調(diào)用處理程序。然后處理程序必須檢查發(fā)送了多少字節(jié)以及還需要發(fā)送多少字節(jié)。然后,它必須再次調(diào)用 async_write_some()。使用 boost::asio::async_write() 可以避免重復(fù)計算要發(fā)送的字節(jié)數(shù)和調(diào)用 async_write_some()。使用此函數(shù)開始的異步操作僅在數(shù)據(jù)中的所有字節(jié)都發(fā)送完畢后才完成。
發(fā)送數(shù)據(jù)后,會調(diào)用 write_handler()。該函數(shù)使用參數(shù) boost::asio::ip::tcp::socket::shutdown_send 調(diào)用shutdown(),表示程序已完成通過套接字發(fā)送數(shù)據(jù)。由于沒有待處理的異步操作,示例 32.6 退出。請注意,雖然 data 僅在 accept_handler() 中使用,但它不能是局部變量。數(shù)據(jù)通過 boost::asio::buffer() 引用傳遞到 boost::asio::async_write()。當(dāng) boost::asio::async_write() 和 accept_handler() 返回時,異步操作已開始,但尚未完成。數(shù)據(jù)必須存在,直到異步操作完成。如果數(shù)據(jù)是一個全局變量,這是有保證的。
練習(xí)
開發(fā)可以將文件從一臺計算機(jī)傳輸?shù)搅硪慌_計算機(jī)的客戶端和服務(wù)器。當(dāng)服務(wù)器啟動時,它應(yīng)該顯示所有本地接口的 IP 地址列表并等待客戶端連接。當(dāng)客戶端啟動時,來自服務(wù)器的 IP 地址和本地文件的名稱應(yīng)作為命令行選項傳遞。客戶端應(yīng)將文件傳輸?shù)椒?wù)器,服務(wù)器將其保存到當(dāng)前工作目錄。在傳輸期間,客戶端應(yīng)該顯示某種進(jìn)度指示器,以便用戶知道傳輸正在進(jìn)行中。使用回調(diào)實現(xiàn)客戶端和服務(wù)器。
到此這篇關(guān)于C++網(wǎng)絡(luò)編程詳細(xì)講解的文章就介紹到這了,更多相關(guān)C++網(wǎng)絡(luò)編程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c語言中字符串函數(shù)(庫函數(shù)使用)和模擬實現(xiàn)圖文教程
C語言中對字符和字符串的處理很是頻繁,但是C語言本身并沒有字符串類型,這篇文章主要給大家介紹了關(guān)于c語言中字符串函數(shù)(庫函數(shù)使用)和模擬實現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-01-01C++深入淺出講解內(nèi)存四區(qū)與new關(guān)鍵字的使用
內(nèi)存四區(qū),一個非常重要的知識點,搞懂了內(nèi)存四區(qū),才能更快的去搞懂指針。我們寫的C語言代碼,不夸張的說,都是直接或者間接的在操作內(nèi)存。C語言之所以能夠開發(fā)操作系統(tǒng),就是指針的存在,而指針說白了就是地址,內(nèi)存地址,指針變量說白了就是存儲地址的變量2022-05-05