ip::tcp的内部类型socket、acceptor和resolver是asio库TCP通信中最核心的一组类,它们封装了socket的连接、断开和数据收发功能,使用它们可以很容易地编写出socket程序。
socket类是TCP通信的基本类,调用成员函数connect()可以连接到一个指定的通信端点,连接成功后用local_endpoint()和remote_endpoint()获得连接两端的端点信息,用read_some()和write_some()阻塞读写数据,当操作完成后使用close()函数关闭socket。如果不关闭socket,那么在socket对象析构时也会自动调用close()关闭。
acceptor类对应socketAPI的accept()函数功能,它用于服务器端,在指定的端口号接受连接,必须配合socket类才能完成通信。
resolver类对应socketAPI的getaddrinfo()系列函数,用于客户端解析网址获得可用的IP地址,解析得到的IP地址可以使用socket对象连接。
下面是一个使用socket类和acceptor类来实现一对同步通信的服务器和客户端程序:
服务器端(它使用一个acceptor对象在6688端口接受连接,当有连接时使用一个socket对象发送一个字符串):
同步
socket
处理
服务器端:
server.cpp
#include "boost/asio.hpp"
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "iostream"
using namespace std;
int main()
{
try //function-try
块
{
cout << "server start" << endl;
boost::asio::io_service ios; //asio
程序必须的
io_service
对象
boost::asio::ip::tcp::acceptor acceptor(ios, //
创建
acceptor
对象,
iPv4
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 7001)); //
接受
7001
端口
cout << acceptor.local_endpoint().address() << endl;
while (true) //
执行循环服务
{
boost::asio::ip::tcp::socket sock(ios); //
一个
socket
对象
acceptor.accept(sock); //
阻塞等待
socket
连接
cout << "client : ";
cout << sock.remote_endpoint().address() << endl;
sock.write_some(boost::asio::buffer("hello asio")); //
发送数据
}
}
catch (std::exception& e) //
捕获可能发生的异常
{
cout << e.what() << endl;
}
return 0;
}
注:要注意的是自由函数
buffer()
,它可以包装很多种类的容器成为
asio
组件可用的缓存区类型。
通常我们不能把数组、
vector
等容器用作
asio
的读写参数,必须使用
buffer()
函数包装。
客户端
client.cpp
#include "boost/asio.hpp"
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "iostream"
using namespace std;
#include "vector"
class a_timer
{
public:
template<typename F> //
模板类型,可以接受任意可调用物
a_timer(boost::asio::io_service& ios, int x, F func)
:f(func), count_max(x), count(0), //
初始化回调函数和计数器
t(ios, boost::posix_time::millisec(500)) //
启动计时器
{
t.async_wait(boost::bind(&a_timer::call_func, //
异步等待计时器
this, boost::asio::placeholders::error)); //
注册回调函数
}
void call_func(const boost::system::error_code& )
{
if (count >= count_max) //
如果计数器达到上限则返回
{ return; } //
++count;
f(); //
调用
function
对象
//
设置定时器的终止时间为
0.5
秒之后
t.expires_at(t.expires_at() + boost::posix_time::microsec(500));
//
再次启动定时器,异步等待
t.async_wait(boost::bind(&a_timer::call_func,this, boost::asio::placeholders::error));
}
private:
int count; //
计时器成员变量
int count_max;
boost::function<void()> f; // function
对象,持有无参无返回值的可调用物
boost::asio::deadline_timer t; // asio
定时器对象
};
void client(boost::asio::io_service& ios) //
传入
io_service
对象
{
try
{
cout << "client start." << endl;
boost::asio::ip::tcp::socket sock(ios); //
创建
socket
对象
//
创建连接端点
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 7001);
sock.connect(ep); //socket
连接到端点
vector<char> str(100, 0); //
定义一个
vector
缓冲区
sock.read_some(boost::asio::buffer(str)); //
使用
buffer()
包装缓冲区接收数据
cout << "recive from" << sock.remote_endpoint().address();
cout << &str[0] << endl; //
输出接收的字符串
}
catch (std::exception& e)
{ cout << e.what() << endl; } //
捕获可能发生的异常
}
//
然后在
main()
函数中创建
io_service
对象,用启动器启动
socket
客户端:
int main()
{
boost::asio::io_service ios;
a_timer at(ios, 5, boost::bind(client, boost::ref(ios)));
ios.run(); //
启动定时器
return 0;
}
异步socket
处理
服务器端:
server.cpp
#include "boost/asio.hpp"
//#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/bind.hpp"
//#include "boost/function.hpp"
#include "iostream"
using namespace std;
using namespace boost;
//首先定义一个server类,它实现异步服务的所有功能
//server类必须的成员变量是io_service对象和acceptor对象,他们的是TCP通信的必备要素
//还定义了一个只能指针的typedef,它指向socket对象,用来在回调函数中和传递。
class server
{
private:
boost::asio::io_service &ios;
boost::asio::ip::tcp::acceptor acceptor;
typedef boost::shared_ptr<boost::asio::ip::tcp::socket> sock_pt;
//server的构造函数存储io_service对象,使用ios、tcp协议和端口号初始化acceptor对象
//并用start()函数立即启动异步服务
public:
server(boost::asio::io_service& io) : ios(io), acceptor(ios,boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),7004))
{ start(); }
//start()函数用于启动异步接受连接,需要调用acceptor的async_accept()函数。
//为了让socket对象能够被异步调用后还能使用,我们必须使用shared_ptr来创建socket对象的
//智能指针,他可以在程序的整个生命周期中存在,直到没人使用它为止。
void start()
{
sock_pt sock(new boost::asio::ip::tcp::socket(ios)); //智能指针
acceptor.async_accept(*sock,boost::bind(&server::accept_handler,this,boost::asio::placeholders::error,sock)); //异步侦听服务
}
//当有TCP连接发生时,server::accept_handler()函数将被调用,它使用socket对象发送数据
void accept_handler(const boost::system::error_code& ec,sock_pt sock)
{
if(ec) //检验错误码
{ return; }
cout << "client:"; //输出连接的客户端信息
cout << sock->remote_endpoint().address() << endl;
sock->async_write_some(boost::asio::buffer("hello asio"),
boost::bind(&server::write_handler, this,boost::asio::placeholders::error));
start(); //再次启动异步接受连接
}
//首先它必须检测asio传递的error_code,保证没有错误发生。然后调用socket对象的
//async_write_some()异步发送数据。同样,我们必须再为这个异步调用编写回调函数
//write_handler()。当发送完数据后不要忘记调用start()再次启动服务器接受连接,
//否则当完成数据发送后io_service将因为没有时间处理而结束运行。
//发送数据的回调函数write_handler()很简单,可以直接实现一个空函数,这里输出一条信息
//表示异步发送数据完成
void write_handler(const boost::system::error_code& error)
{ cout << "send msg complete," <<endl; }
};
//最后在main()函数中创建io_service对象和server对象,调用run()方法开始异步等待
int main()
try
{
cout << "server start." <<endl;
boost::asio::io_service ios; //io_service对象
server serv(ios); //构造server对象
ios.run();
}
catch (std::exception& e) //捕获可能发生的异常
{
cout << e.what() <<endl;
}
客户端:
client.cpp
//通常客户端不需要使用异步通信,出于学习,这里也实现异步的客户端,示范asio的异步用法。
#include<iostream>
#include<boost/asio.hpp>
#include<boost/bind.hpp>
using namespace std;
//与服务器端对象,这里需要定义一个client类,实现所有异步调用功能
//client与server类相同,都必须持有一个io_service的应用,也要有一个socket的share_ptr
//不同的是client不需要acceptor,而是使用一个端点直接与服务器建立连接。
class client
{
private:
boost::asio::io_service& ios; //io_service对象
boost::asio::ip::tcp::endpoint ep; //TCP端点
typedef shared_ptr<boost::asio::ip::tcp::socket> sock_pt;
//client构造函数的主要作用是初始化IP端点对象,并调用start()函数启动TCP连接
public:
client(boost::asio::io_service& io):ios(io),
ep(boost::asio::ip::address::from_string("127.0.0.1"),7004)
{ start(); } //启动异步连接
//start()创建一个socket独享的智能指针以便在异步调用过程中传递,
//然后使用async_connect()启动一个异步连接,指定连接的处理函数是conn_handler()
void start()
{
sock_pt sock(new boost::asio::ip::tcp::socket(ios));
sock->async_connect(ep,boost::bind(&client::conn_handler, this, boost::asio::placeholders::error, sock));
}
//当异步连接成功时,conn_handler()将被调用,它再用shared_ptr包装vector
//用buffer()函数把vector作为接收数据的缓冲区,由async_read_some()异步读取
//然后再启动一个异步连接
void conn_handler(const boost::system::error_code& ec, sock_pt sock)
{
if(ec) //处理错误代码
{ return ; }
cout << "revice from" << sock->remote_endpoint().address();
boost::shared_ptr<vector<char> > str(new vector<char>(100,0)); //建立接收数据的缓冲区
sock->async_read_some(boost::asio::buffer(*str), //异步读取数据
boost::bind(&client::read_handler, this, boost::asio::placeholders::error, str));
start(); //再次启动异步连接
}
//当异步读取结束时read_handler()被调用,它直接输出shared_ptr指向的缓冲区内容
void read_handler(const boost::system::error_code& ec,
boost::shared_ptr<vector<char> > str)
{
if(ec) //处理错误代码
{ return; }
cout << &(*str)[0] <<endl; //输出接收到的数据
}
};
//客户端的main()函数代码与服务器端的完全一致
int main()
{
try
{
cout << "client start." << endl;
boost::asio::io_service ios;
client c1(ios);
ios.run();
}
catch(std::exception& e) //捕获可能发生的异常
{
cout << e.what() <<endl;
}
}