boost-asio学习2——同步、异步socket处理

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;
}
}

猜你喜欢

转载自blog.csdn.net/qq_23348071/article/details/74548356