Boost(七)——異步輸入輸出

結合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個字節的分隔符。這個我還沒有解決。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章