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

时间:2021-12-14 05:16:32
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;}}