結合Boost官網
異步任務的典型例子是網絡應用。
不要綁定多個線程到單個 I/O 服務,而是創建多個 I/O 服務。 然後每一個 I/O 服務使用一個線程。 如果 I/O 服務的數量與系統的處理器內核數量相匹配,則異步操作都可以在各自的內核上執行。
網絡通信流程
客戶端:
boost::asio::io_serveice建立服務 ——> boost::asio::ip::tcp::resolver解析網站 ——> 成功boost::system::err_code 置爲0 ——> 連接解析域名(connect) ——> 向服務器發送請求(write)——> 接受應答(read)。
服務端:
boost::asio::io_serveice建立服務 ——> boost::asio::ip::tcp::socket建立套接字 ——> boost::asio::ip::tcp::acceptor構建“接受器”(boost::asio::ip::tcp::endpoint設置通信模式(ipv4 或 ipv6)和端口號(設置成80)) ——> acceptor.listen()處於阻塞監聽,等待客戶端連接 ——> acceptor.async_accept(socket, write_handler)接受到連接信號,構建套接字通信 ——> write_handler()向客戶端發送信息
一般的,一個socket由一個I/O服務管理,而一個I/O服務可以管理多個socket
糾正 <<開發 Boost.Asio 擴展中>> 的錯誤
basic_timer類中 this->service 和 this->implementation 改成 this->get_service() 和 this->get_implementation()
因爲service 和 implementation是私有變量,需要用get方法獲得。
源demo:
#include <boost/asio.hpp>
#include <cstddef>
template <typename Service>
class basic_timer
: public boost::asio::basic_io_object<Service>
{
public:
explicit basic_timer(boost::asio::io_service &io_service)
: boost::asio::basic_io_object<Service>(io_service)
{
}
void wait(std::size_t seconds)
{
return this->service.wait(this->implementation, seconds);
}
template <typename Handler>
void async_wait(std::size_t seconds, Handler handler)
{
this->service.async_wait(this->implementation, seconds, handler);
}
};
修正:
#include <boost/asio.hpp>
#include <cstddef>
template <typename Service>
class basic_timer
: public boost::asio::basic_io_object<Service>
{
public:
explicit basic_timer(boost::asio::io_service &io_service)
: boost::asio::basic_io_object<Service>(io_service)
{
}
void wait(std::size_t seconds)
{
return this->get_service().wait(this->get_implementation(), seconds);
}
template <typename Handler>
void async_wait(std::size_t seconds, Handler handler)
{
this->get_service().async_wait(this->get_implementation(), seconds, handler);
}
};
講一下流程
源main.cpp demo
#include <boost/asio.hpp>
#include <iostream>
#include "basic_timer.hpp"
#include "timer_impl.hpp"
#include "basic_timer_service.hpp"
void wait_handler(const boost::system::error_code &ec)
{
std::cout << "5 s." << std::endl;
}
typedef basic_timer<basic_timer_service<> > timer;
int main()
{
boost::asio::io_service io_service;
timer t(io_service);
t.async_wait(5, wait_handler);
io_service.run();
system("pause");
}
1、創建I/0服務
2、創建basic_timer<basic_timer_service<timer_impl> >類對象:t
3、設置 t的async_wait函數 :延時5s後執行wait_handler
basic_timer類的async_wait()函數如下
template <typename Handler>
void async_wait(std::size_t seconds, Handler handler)
{
this->get_service().async_wait(this->get_implementation(), seconds, handler);
}
其中:
get_service()獲得的是basic_timer_service類對象
this->get_service().async_wait()是basic_timer_service類的async_wait函數
this->get_implementation()獲得的是timer_impl類對象
4、進入basic_timer_service類的async_wait()函數:
template <typename Handler>
void async_wait(implementation_type &impl, std::size_t seconds, Handler handler)
{
this->async_io_service_.post(wait_operation<Handler>(impl, this->get_io_service(), seconds, handler));
}
通過operator()()函數 實現對象函數
如下
①impl_ 類型爲 boost::weak_ptr<> 弱指針,lock()用來判定是否對象存在(未被destruct)
如果存在則返回一個共享指針,否則爲0
②impl->wait()函數執行的是 timer_tmpl類的wait函數 即等待了seconds秒
③post函數:告訴I/O服務端要執行的函數 handler 是 一開始的 main.cpp文件的wait_handler函數
void operator()() const
{
implementation_type impl = impl_.lock();
if (impl)
{
boost::system::error_code ec;
impl->wait(seconds_, ec);
this->io_service_.post(boost::asio::detail::bind_handler(handler_, ec));
}
else
{
this->io_service_.post(boost::asio::detail::bind_handler(handler_, boost::asio::error::operation_aborted));
}
}
5、io_service.run():服務端運行
練習題:
1、修改 第 7.4 節 “網絡編程” 中的服務器程序,不在一次請求後即終止,而是可以處理任意多次請求。
#include <boost/asio.hpp>
#include <string>
boost::asio::io_service io_service;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 80);
boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint);
boost::asio::ip::tcp::socket sock(io_service);
std::string data = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, world!";
void write_handler(const boost::system::error_code &ec, std::size_t bytes_transferred)
{
}
void accept_handler(const boost::system::error_code &ec)
{
if (!ec)
{
boost::asio::async_write(sock, boost::asio::buffer(data), write_handler);
}
}
int main()
{
acceptor.listen();
acceptor.async_accept(sock, accept_handler);
io_service.run();
}
解答:
利用共享指針控制socket,實現I/O服務管理一個socket,在I/O服務有管理的socket時,acceptor處於阻塞狀態。
#include <boost/asio.hpp>
#include <string>
#include <iostream>
#include <boost\shared_ptr.hpp>
#include <boost\bind.hpp>
boost::asio::io_service io_service;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 80);
boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint);
typedef boost::shared_ptr<boost::asio::ip::tcp::socket> sock_ptr;
typedef boost::asio::ip::tcp::socket sock_type;
std::string data = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, world!";
void start();
void write_handler(const boost::system::error_code &ec, std::size_t bytes_transferred)
{
std::cout << "link success" << std::endl;
}
void accept_handler(const boost::system::error_code &ec, sock_ptr sock_)
{
if (!ec)
{
boost::asio::async_write(*sock_, boost::asio::buffer(data), write_handler);
}
start();
}
void start()
{
sock_ptr sock(new sock_type(io_service));
acceptor.async_accept(*sock, boost::bind(accept_handler,
boost::asio::placeholders::error, sock));
}
int main()
{
start();
acceptor.listen();
io_service.run();
system("pause");
}
2、擴展 第 7.4 節 “網絡編程” 中的客戶端程序,即時在所接收到的HTML代碼中分析某個URL。 如果找到,則同時下載相應的資源。 對於本練習,只使用第一個URL。 理想情況下,網站及其資源應被保存在兩個文件中而不是同時寫出至標準輸出流。
ps 原文有錯誤 以下是修正過的。 請求協議是"GET / HTTP/1.1\r\nHost: www.highscore.de\r\n\r\n"
這題涉及到html標籤的解析,後續會補充。
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <iostream>
#include <string>
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::socket sock(io_service);
boost::array<char, 4096> buffer;
void read_handler(const boost::system::error_code &ec, std::size_t bytes_transferred)
{
if (!ec)
{
std::cout << std::string(buffer.data(), bytes_transferred) << std::endl;
sock.async_read_some(boost::asio::buffer(buffer), read_handler);
}
}
void connect_handler(const boost::system::error_code &ec)
{
if (!ec)
{
boost::asio::write(sock, boost::asio::buffer("GET / HTTP/1.1\r\nHost: www.highscore.de\r\n\r\n"));
sock.async_read_some(boost::asio::buffer(buffer), read_handler);
}
}
void resolve_handler(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it)
{
if (!ec)
{
sock.async_connect(*it, connect_handler);
}
}
int main()
{
boost::asio::ip::tcp::resolver::query query("www.highscore.de", "80");
resolver.async_resolve(query, resolve_handler);
io_service.run();
system("pause");
}
3、創建一個客戶端/服務器應用,在兩臺PC間傳送文件。 當服務器端啓動後,它應該顯示所有本地接口的IP地址並等待客戶端連接。 客戶端則應將服務器端的某一個IP地址以及某個本地文件的文件名作爲命令行參數。 客戶端應將該文件傳送給服務器,後者則相應地保存它。 在傳送過程中,客戶端應向用戶提供一些進度的可視顯示。
我把程序放在github開源。
這裏會有一個問題:
客戶端假設一次性發送10M文件,若文件每次超過10M,服務端接受的數據都會多出4個字節的分隔符。這個我還沒有解決。