在前面已经学习到客户端同步连接服务器的处理过程,由于同步的关系,前面的代码理解起来是比较简单,也容易接受,但它有一个致命的缺陷,就是在连接过程、在等服务器回应数据时,CPU不能做别的事情,只能在那里等待。这种等待对于有界面的软件来说,是一种不能接受的表现方式。比如当用户点击连接服务器之后,就一直等在那里,什么也不能操作,就算想立即退出程序也不行,必须等服务器回应之后,或者超时连接之后,才可以操作。对于这样的处理方式,没有用户愿意接受,更加没有用户愿意使用这样的软件,因此采用异步来处理网络的方式,就变得很迫切的要求了。对于现代的软件开发人员,还不掌握这种处理的方式,已经很难适应目前客户对软件的要求了。如果要从底层开发一个异步处理的网络库,还是比较费时间的,boost库提供一个比较完美的解决方案。它可以把域名解释、连接服务器、数据发送和接收,都变成一个事件触发的方式来处理。这样就可以当没有事件响应时处理别的事情,当有事件回应时就可以立即处理网络的事情。具体实现的例子如下:
// boost_023.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <string>
//封装一个客户端类来处理异常网络处理。
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; //接收数据缓冲区。
};
//测试网络服务异步处理。
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里的交互图来表达。