boost库在工作(30)网络客户端之五

在前面已经学习到客户端同步连接服务器的处理过程,由于同步的关系,前面的代码理解起来是比较简单,也容易接受,但它有一个致命的缺陷,就是在连接过程、在等服务器回应数据时,CPU不能做别的事情,只能在那里等待。这种等待对于有界面的软件来说,是一种不能接受的表现方式。比如当用户点击连接服务器之后,就一直等在那里,什么也不能操作,就算想立即退出程序也不行,必须等服务器回应之后,或者超时连接之后,才可以操作。对于这样的处理方式,没有用户愿意接受,更加没有用户愿意使用这样的软件,因此采用异步来处理网络的方式,就变得很迫切的要求了。对于现代的软件开发人员,还不掌握这种处理的方式,已经很难适应目前客户对软件的要求了。如果要从底层开发一个异步处理的网络库,还是比较费时间的,boost库提供一个比较完美的解决方案。它可以把域名解释、连接服务器、数据发送和接收,都变成一个事件触发的方式来处理。这样就可以当没有事件响应时处理别的事情,当有事件回应时就可以立即处理网络的事情。具体实现的例子如下:

// boost_023.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include 
#include 
#include 
#include 
#include 

//封装一个客户端类来处理异常网络处理。
//软件开发人员: 蔡军生  2013-06-16
//
class CClient
{
public:
	CClient(const std::string& strHost, const std::string& strService, 
		boost::asio::io_service& ioService)
		:m_resolver(ioService), m_socket(ioService)
	{		
		//构造HTTP请求发送给服务器.
		std::ostream requestPacket(&m_request);
		requestPacket << "GET " << "http://www.boost.org/" << " HTTP/1.0\r\n";
		requestPacket << "Host: " << "www.boost.org" << "\r\n";
		requestPacket << "Accept: */*\r\n";
		requestPacket << "Connection: close\r\n\r\n";

		//发送解释域名请求。
		boost::asio::ip::tcp::resolver::query query(strHost, strService);
		m_resolver.async_resolve(query,
			boost::bind(&CClient::HandleResolve, this,
			boost::asio::placeholders::error,
			boost::asio::placeholders::iterator));
	}

private:
	//域名解释回应。
	void HandleResolve(const boost::system::error_code& err,
		boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
	{
		if (!err)
		{			
			//准备连接解释后的IP地址, 以便连接到服务器。
			boost::asio::ip::tcp::endpoint endpoint= *endpoint_iterator;
			m_socket.async_connect(endpoint,
				boost::bind(&CClient::HandleConnect, this,
				boost::asio::placeholders::error, ++endpoint_iterator));
		}
		else
		{
			std::cout << "Error: " << err.message() << "\n";
		}
	}

	//连接到服务器成功或者失败。
	void HandleConnect(const boost::system::error_code& err,
		boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
	{
		if (!err)
		{
			//连接到服务器成功,接着发送请求数据给服务器。
			boost::asio::async_write(m_socket, m_request,
				boost::bind(&CClient::HandleWriteRequest, this,
				boost::asio::placeholders::error));
		}
		else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator())
		{
			//连接服务器失败,继续连接下一个服务器的端点。
			m_socket.close();
			boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
			m_socket.async_connect(endpoint,
				boost::bind(&CClient::HandleConnect, this,
				boost::asio::placeholders::error, ++endpoint_iterator));
		}
		else
		{
			std::cout << "Error: " << err.message() << "\n";
		}
	}

	//当发送请求数据给服务器之后处理。
	void HandleWriteRequest(const boost::system::error_code& err)
	{
		if (!err)
		{
			//发送给服务器成功之后,就可以读取服务器回应的数据。
			boost::asio::async_read_until(m_socket, m_response, "\r\n",
				boost::bind(&CClient::HandleReadStatusLine, this,
				boost::asio::placeholders::error));
		}
		else
		{
			std::cout << "Error: " << err.message() << "\n";
		}
	}

	//处理服务器回应的数据完整状态行数据。
	void HandleReadStatusLine(const boost::system::error_code& err)
	{
		if (!err)
		{
			//分析回应的数据。
			std::istream responsePacket(&m_response);
			std::string strhttpVersion;
			responsePacket >> strhttpVersion;
			unsigned int iStatusCode;
			responsePacket >> iStatusCode;

			std::string strStatusMessage;	
			std::getline(responsePacket, strStatusMessage);

			//输出解释出来的数据。
			std::cout << "response: " << strhttpVersion << " "
				<< iStatusCode << " "
				<< strStatusMessage << std::endl;				
		}
		else
		{
			std::cout << "Error: " << err << "\n";
		}
	}
	
private:	
	boost::asio::ip::tcp::resolver m_resolver; //解释域名
	boost::asio::ip::tcp::socket m_socket; //收发数据SOCKET。
	boost::asio::streambuf m_request; //发送数据缓冲区。
	boost::asio::streambuf m_response; //接收数据缓冲区。
};

//测试网络服务异步处理。
//软件开发人员: 蔡军生  2013-06-16
//QQ: 9073204
void Test(void)
{
	//
	const std::string strHost("www.boost.org");
	const std::string strHttp("http");

	//定义IO服务对象。
	boost::asio::io_service ioService;
	CClient client(strHost,strHttp, ioService);
	ioService.run();
}

int _tmain(int argc, _TCHAR* argv[])
{
	Test();
	system("pause");

	return 0;
}

在这个例子里封装一个类CClient来实现客户端与服务器的处理过程。这个类构造时,构造一个域名解释对象query,接着调用域名解释对象的函数async_resolve来发起域名解释异步请求。当域名解释有事件响应时,就会调用函数HandleResolve来处理,在这里解释成功之后,就可以调用SOCKET对象的函数async_connect来进行异步连接服务器。当连接服务器有事件响应时,就会调用函数HandleConnect处理,在这函数里调用async_write函数来对SOCKET进行发送数据给服务器。当服务器有数据回应之后,就会调用函数HandleWriteRequest来处理,然后在这个函数里调用async_read_until函数对SOCKET进行接收数据,当数据接收完成,就调用函数HandleReadStatusLine来处理输出。从这个例子里,可以看到异步处理都是响应式编程,针对事件来处理的,比起同步处理要复杂很多,理解起来要艰难很多。如果要很清楚地理解这种相互的操作,最适合使用UML里的交互图来表达。


你可能感兴趣的:(学习boost)