第13章 网络 Page749~755 asio核心类 ip::tcp::acceptor

4,ip::tcp::acceptor

ip::tcp::socket类提供用于客户端 async_connect()(发起连接)方法,但没有提供服务端
用于assync_accept()(接受连接)的方法;这正对应了客户端和服务端之间的"多对一"关系

每一个socket都可以主动发起连接,但连接的目标可以是同一个服务端; 倒过来,就是一个
服务端可以接受并拥有多个客户端发起的连接。

ip::tcp::acceptor(接收器)就负责一件事:当有新客户端发起连接请求时,acceptor
决定是否接受

接受,则建立连接,不接受则连接被扼杀在摇篮里。

ip::tcp::acceptor 类的构造函数的常用版本声明如下:

acceptor(io_service& ios, endpoint_type const& endpoint
                            , bool reuse_addr = true);

服务端的"接受器"当然是一个I/O对象,所以第一个入参是io_service对象
第二个入参指明当前服务的监听地址endpoint
最后一个入参指明端口是否可复用,如果不懂什么意思,就取默认值
构造过程会自动执行服务端的监听(listen)动作

acceptor“接收器”最重要的方法

async_accept(),它还有一个同步版本accept(),因为很少有不在意
性能的网络服务端应用,所以我们终点讲异步版本

async_accept()又有两个常用版本,二者只差一个入参:

//版本一:三个入参
void async_accept(ip::tcp::socket& peer_socket
                 , endpoint_type& peer_endpoint
				 ,AcceptHandler handler);
				 
//版本二;两个入参(没有peer_endpoint)
void async_accept(ip::tcp::socket& peer_socket
                    , AcceptHandler handler);

版本二不提供第二个入参peer_endpoint, 可视为版本一的简化,我们以版本一作为讲解对象

第一个入参 peer_socket表示来自对端的网络套接字
当客户端发起连接,它会沿着网线往服务端传来一个C++变量,并且区分为传值传址两种方式。这个参数作为入参传递给async_accept()函数,此时还没有接受到任何连接请求,因此该变量
只是服务端的程序预备用来存储未来可能有的新连接

第二个入参peer_endpoint和peer_socket类似,同样是需要服务端在函数调用前,事先定义的
一个变量。等到确实有客户端新请求到来是,用它来存储客户端的地址(主机地址+端口号)

当有新连接请求时,第三个入参所代表的回调操作被触发,该回调原型为:

void handler(const boost::system::error_code& error);

ip::tcp::resolver::async_resolve()的回调函数好歹传回了解析的结果resolver::iterator iterator,

但 ip::tcp::async_accept() 的回调又只是传回是否出错的结果,代表网络连接的那个套接字变量
peer_socket上哪儿去了?那个代表客户端地址的peer_endpoint又上哪儿去了?

在对比asio与licurl的差异时,我们就已经说过了,前者需要我们自行维护好这些数据。之前my_resolver程序在解析出ip地址后,会以客户端的身份,向该地址以及用户输入的端口所代表的目标,尝试发起连接,若成功,就输出“成功。”

DumbServer(哑巴服务器):

DumbServer类只有一个成员数据,即ip::tcp::acceptor。先看构造函数:

class DumbServer
{
public:
    DumbServer(boost::asio::io_service& ios, char const* host
                                , unsigned short port)
        : _acceptor(ios, make_endpoint(host, port))
    {
         cout << "哑巴服务运行在:" 
            << host << " : "<< port << endl;
         cout << "按 Ctrl - C 退出。" << endl;
    }
    
private:
    boost::asio::ip::tcp::acceptor _acceptor;
};

 acceptor和其他“I/O类”一样,构造时,io_service入参必须以引用方式传入,因成员“_acceptor”必须在DumbServer初始化列表中完成构造,而它需要一个io_service对象。

第二个入参服务监听的地址endpoint,此处需要通过一个工具函数make_endpoint()来创建。

DumbServer唯一的普通成员函数Start():

void Start()
{
    boost::asio::io_service& ios = 
                _acceptor.get_io_service();
    //存储客户端发来的连接
    auto peer_socket = 
        make_shared<boost::asio::ip::tcp::socket>(ios);
    //存储客户端的端点
    auto peer_endpoint = 
        make_shared<boost::asio::ip::tcp::endpoint>();

Start()方法是要让"_acceptor"执行async_accept(...)方法。

peer_scoket和peer_endpoint都被定义为shared_ptr它们将存活着,当有新连接产生时,它们一个用来存储网络连接底下的套接字一个用来存储网络连接对端的地址;直到连接被断开不再需要时,链式传递结束,二者自动释放。

重点来了,看看async_accept()方法如何调用:

    _acceptor.async_accept(*peer_socket, *peer_endpoint
            , [peer_socket, peer_endpoint, this]
                (boost::system::error_code const& err)
    {
        if(err)
        {
            cout << err.message() << endl;
        }

        cout << "客户端为:" 
             <<(*peer_endpoint).address().to_string()
             << " : " << (*peer_endpoint).port() << endl;
        //链式任务反应,确保一直在监听处理
        this->Start();
    });
}//end Start()

async_accept()前两个入参都是普通对象的应用,

第一个入参类型是“ip::tcp::socket&”,第二个是“endpoint_type const&”,我们刚刚创建shared_ptr智能指针,所以只能乖乖地分别通过‘*’取值。

这也暗示了我们,将二者传递给async_accept()函数并不是一次链式传递。

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值