c++ boost库异步网络编程 – ASIO库
标签(空格分隔): c++ boost
概述
Boost C++ 库 Asio,它是异步输入输出的核心。 名字本身就说明了一切:Asio 意即异步输入/输出。该库可以让 C++ 异步地处理数据,且平*立。异步数据处理就是指,任务触发后不需要等待它们完成。 相反,Boost.Asio 会在任务完成时触发一个应用。异步任务的主要优点在于,在等待任务完成时不需要阻塞应用程序,可以去执行其它任务。
异步任务的典型例子是网络应用。 如果数据被发送出去了,比如发送至 Internet,通常需要知道数据是否发送成功。 如果没有一个象 Boost.Asio 这样的库,就必须对函数的返回值进行求值。 但是,这样就要求待至所有数据发送完毕,并得到一个确认或是错误代码。 而使用 Boost.Asio,这个过程被分为两个单独的步骤:第一步是作为一个异步任务开始数据传输。 一旦传输完成,不论成功或是错误,应用程序都会在第二步中得到关于相应的结果通知。 主要的区别在于,应用程序无需阻塞至传输完成,而可以在这段时间里执行其它操作。
基本概念
IP地址
对于IP地址的处理,Boost.Asio提供了ip::address , ip::address_v4和ip::address_v6类。 它们提供了相当多的函数。下面列出了最重要的几个:
ip::address(v4_or_v6_address):这个函数把一个v4或者v6的地址转换成ip::address
ip::address:from_string(str):这个函数根据一个IPv4地址(用.隔开的)或者一个IPv6地址(十六进制表示)创建一个地址。
ip::address::to_string() :这个函数返回这个地址的字符串。
ip::address_v4::broadcast([addr, mask]):这个函数创建了一个广播地址 ip::address_v4::any():这个函数返回一个能表示任意地址的地址。
ip::address_v4::loopback(), ip_address_v6::loopback():这个函数返回环路地址(为v4/v6协议)
ip::host_name():这个函数用string数据类型返回当前的主机名。
大多数情况你会选择用ip::address::from_string:
ip::address addr = ip::address::from_string("127.0.0.1");
如果你想通过一个主机名进行连接,下面的代码片段是无用的:
// 抛出异常
ip::address addr = ip::address::from_string("www.yahoo.com");
端点
端点是使用某个端口连接到的一个地址。不同类型的socket有它自己的endpoint类,比如ip::tcp::endpoint、ip::udp::endpoint和ip::icmp::endpoint
如果想连接到本机的80端口,你可以这样做:
ip::tcp::endpoint ep( ip::address::from_string(“127.0.0.1”), 80);
有三种方式来让你建立一个端点:
endpoint():这是默认构造函数,某些时候可以用来创建UDP/ICMP socket。
endpoint(protocol, port):这个方法通常用来创建可以接受新连接的服务器端socket。
endpoint(addr, port):这个方法创建了一个连接到某个地址和端口的端点。
例子如下:
ip::tcp::endpoint ep1;
ip::tcp::endpoint ep2(ip::tcp::v4(), 80);
ip::tcp::endpoint ep3( ip::address::from_string("127.0.0.1), 80);
解析器
如果你想连接到一个主机(不是IP地址),则需要通过解析器解析给定的ip和端口或者域名,返回解析出来的端点集合,可以通过迭代,拿到所有与域名关联的iP:
// 输出 "87.248.122.122"
io_service service;
ip::tcp::resolver resolver(service);
ip::tcp::resolver::query query("www.yahoo.com", "80");
ip::tcp::resolver::iterator iter = resolver.resolve( query);
ip::tcp::endpoint ep = *iter;
std::cout << ep.address().to_string() << std::endl;
你可以用你需要的socket类型来替换tcp。首先,为你想要查询的名字创建一个查询器,然后用resolve()函数解析它。如果成功,它至少会返回一个入口。你可以利用返回的迭代器,使用第一个入口或者遍历整个列表来拿到全部的入口。
给定一个端点,可以获得他的地址,端口和IP协议(v4或者v6):
std::cout << ep.address().to_string() << ":" << ep.port()
<< "/" << ep.protocol() << std::endl;
套接字
Boost.Asio有三种类型的套接字类:ip::tcp, ip::udp和ip::icmp。当然它也是可扩展的,你可以创建自己的socket类,尽管这相当复杂。如果你选择这样做,参照一下boost/asio/ip/tcp.hpp, boost/asio/ip/udp.hpp和boost/asio/ip/icmp.hpp。它们都是含有内部typedef关键字的超小类。
你可以把ip::tcp, ip::udp, ip::icmp类当作占位符;它们可以让你便捷地访问其他类/函数,如下所示:
ip::tcp::socket, ip::tcp::acceptor, ip::tcp::endpoint,ip::tcp::resolver, ip::tcp::iostream
ip::udp::socket, ip::udp::endpoint, ip::udp::resolver
ip::icmp::socket, ip::icmp::endpoint, ip::icmp::resolver
socket类创建一个相应的socket。而且总是在构造的时候传入io_service实例:
io_service service;
ip::udp::socket sock(service)
sock.set_option(ip::udp::socket::reuse_address(true));
每一个socket的名字都是一个typedef关键字
ip::tcp::socket = basic_stream_socket
ip::udp::socket = basic_datagram_socket
ip::icmp::socket = basic_raw_socket
Demo 服务端
#include <boost\asio.hpp>
#include <boost\make_shared.hpp>
#include <boost\thread.hpp>
#include <boost\bind.hpp>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
using namespace boost::asio;
using ip::tcp;
class CAsyServer
{
typedef boost::shared_ptr<ip::tcp::socket> socket_ptr;
public:
CAsyServer(io_service& server, short port)
:m_server(server)
, m_acceptor(m_server, ip::tcp::endpoint(ip::tcp::v4(),port))
{
}
//启动服务器
void startService()
{
cout <<"本地监听地址 IP ["<<m_acceptor.local_endpoint().address() <<"]"<<endl;
PostAcceptRequest();
}
private:
void PostAcceptRequest()
{
socket_ptr pClient(new ip::tcp::socket(m_server));
m_acceptor.async_accept(*pClient, boost::bind(&CAsyServer::accept_complete,
this, //成员函数的第一参数是this指针
pClient,
_1));
}
void PostReadRequest(socket_ptr& pClient)
{
pClient->async_read_some(buffer(m_szReadBuff),
boost::bind(&CAsyServer::Read_complete,
this,
pClient,
_1));
}
private:
void accept_complete(socket_ptr& pSocket, const boost::system::error_code& err)
{
if (err)
{
cout << "accept_complete error" << endl;
return;
}
PostAcceptRequest();
cout << "开始读取数据" << endl;
PostReadRequest(pSocket);
}
void write_complete(socket_ptr& pSocket, const boost::system::error_code& err)
{
if (err)
{
cout << "error" << endl;
return;
}
}
void Read_complete(socket_ptr& pSocket, const boost::system::error_code& err)
{
if (err)
{
cout << "Read_complete error" << endl;
cout << err.message() << endl;
cout << err.value() << endl;
return;
}
printf("from[%s]:%s\n", pSocket->remote_endpoint().address().to_string().c_str(),m_szReadBuff);
PostReadRequest(pSocket);
}
private:
io_service& m_server;
ip::tcp::acceptor m_acceptor;
char m_szReadBuff[2048];
};
int _tmain(int argc, _TCHAR* argv[])
{
try
{
io_service server;
CAsyServer service(server, 2001);
service.startService();
server.run();
}
catch (std::exception& _e)
{
cout << "异常处理" << endl;
std::cout << _e.what() << std::endl;
}
std::cout << "server end." << std::endl;
return 0;
}
Demo 客户端
#include <iostream>
#include <boost\shared_ptr.hpp>
#include <boost\asio.hpp>
#include <boost\bind.hpp>
using namespace std;
using namespace boost::asio;
typedef boost::shared_ptr<ip::tcp::socket> sock_ptr;
void write_handler(sock_ptr& psock, const boost::system::error_code & ec);
struct myPacket
{
int mainID;
int subID;
char szname[64];
char szSex[6];
};
void connect_handler(sock_ptr& psock, const boost::system::error_code & ec) {
// 如果ec返回成功我们就可以知道连接成功了
if (ec)
{
cout << "connect error" << endl;
return;
}
for (int i = 0; i < 500; i++)
{
char szbuf[512] = "这是一个连接\0";
psock->async_write_some(buffer(szbuf), boost::bind(write_handler, psock, _1));
}
}
void write_handler(sock_ptr& psock, const boost::system::error_code & ec)
{
try
{
// 如果ec返回成功我们就可以知道连接成功了
if (ec)
{
cout << "write_handler error " << ec.message() << endl;
return;
}
char szBuff[128];
cin >> szBuff;
psock->async_write_some(buffer(szBuff), boost::bind(write_handler, psock, _1));
}
catch (std::exception& e)
{
cout << e.what() << endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
try
{
io_service service;
ip::tcp::endpoint ep(ip::address::from_string("192.168.123.187"), 2001);
sock_ptr psock(new ip::tcp::socket(service));
psock->async_connect(ep, boost::bind(connect_handler, psock, _1));
service.run();
}
catch (std::exception &e)
{
cout << e.what() << endl;
}
catch (...)
{
cout << "位置错误" << endl;
}
return 0;
}