解析网络和协程

resolver

resolver类对应Socket API的getaddrinfo()系列函数,用于解析网址获得可用的IP地址,解析得到的IP地址可以使用socket对象连接。
之前关于TCP通信的所有论述我们都是使用直接的IP地址,但在实际生活中大多数时候我们不可能知道socket 连接另一端的地址,而只有一个域名,这时候我们就需要使用resolver类来通过域名获得可用的IP,它可以实现与IP版本无关的网址解析

template<typename InterProtocol,typename... as>

class basic_resolver
{
    public:
    typedef InterProtocol    protocol_type;
    typedef typename InterProtocol::endpoint endpoint_type;
    typedef basic_resolver_query<InterProtocol> query;
    typedef basic_resolver_iterator<InterProtocol> iteraror;

    explicit basic_resolver(io_service&io_service);

    void cancel();
    iteraror resolve(const query&q);
    
    iteraror resolve(const endpoint_type&e);




};
template<typename InternetProtocol>
class basic_revolve_query
{
     typedef InternetProtocol    protocol_type;
     basic_resolver_query(const std::string&service,
     flags resolve_flags=passive|address_configured)
     ...
};

resolver使用内部类query和 iterator共同完成查询IP地址的工作:首先使用网址和服务名(通常是端口号)创建query对象,然后由resolve ()成员函数生成iterator对象,它代表了查询到的IP端点。之后就可以使用socket对象尝试连接,直到找到一个可用的为止。
在这里我们使用一个函数resolve_connect ()来封装resolver的调用过程,代码如下:

void resolve_connect(ip::tcp::socket&sock,const char*name,int port)
{
    ip::tcp::resolver r(sock.get_io_service());
    ip::tcp::resolver::query q(name,lexical_cast<string>(port));

    auto iter=r.resolve(q);
    decltype(iter) end;
    error_code ec=error::host_not_found;
    for(;ec&&iter!=end;++iter)
    {
        sock.close();
        sock.connect(*iter,ec);

    }
    if(ec)
    {
        cout<<"can't connect"<<endl;
        throw system_error(ec);
    }
    cout<<"connect success"<<endl;
}
int main()
{
    try{
        io_service io;
        ip::tcp::socket sock(io);
        resolve_connect(sock,"www.baidu.com",80);
        cout<<sock.remote_endpoint()<<endl;
    }
    catch(std::exception&e){
        cout<<e.what()<<endl;
        
    }
}

resolve_connect ()函数中使用了lexical_cast,这是因为query对象只接受字符串参数,所以我们需要把端口号由整数转换为字符串。
当开始resolver的迭代时,我们需要使用error_code和逾尾迭代器两个条件来控制循环,因为有可能迭代完所有解析到的端点都无法连接,只有当error_code为0才表示连接成功。
有了resolve_connect()函数,我们就可以不受具体IP地址值的限制,以更直观更灵活的域名来连接服务器

协程

协程(coroutine)是泛化的例程(routine)。例程只有一个入口和多个出口,C++里的函数就是这样,从最开始的函数入口开始,可以在某个时刻用return返回,例程就结束了。而协程则不同,它可以有多个入口多个出口,从最开始的入口进入之后可以随时用yield调用返回,之后再调用协程就会从刚才返回的地方继续执行。现代的很多编程语言都已经内置了协程支持,其中最著名的就是Lua。

类摘要

template<typename Handler>
class basic_yield_context
{
    public:
    basic_yield_context(
        const detail::weak_ptr<callee_typr>&coro,
        caller_type&ca,Handler& handler

    );
    basic_yield_context operator[](
        boost::system::error_code&ec
    )const;
    detail::weak_ptr<callee_type>coro_;
    ...

yield_context的接口很简单,它保存了协程的运行环境,交替执行主协程(caller)和从协程(callee),达到异步的目的。operator[]用于外部获取发生的错误码,如果不使用operator[]则会抛出system_error异常未报告错误。

通常我们不直接创建yield_context对象,而是使用函数spawn ( )。它产生
yield_context对象,再传递给使用yield_context的函数。spawn()有多个重载形式,常用的是如下两种:

void spawn(strand s,Function function);
void spawn(io_service io,Function function);

function必须符合如下的函数签名

void func(boost::asio::yield_context yield);

用法:
在 asio 的协程用法里yield_context取代了我们之前使用的回调handler,async_xxx()函数不需要再写回调函数,而是使用yield _context对象。async_xxx ()调用后会自动交出控制权,执行其他的协程,直到异步操作完成时才会返回继续执行之后的动作,异步读写操作的字节数可以从async_xxx( )的返回值获取。整个程序的流程很像是同步模式,但因为有协程的存在实际上是异步而高效的。

int main()
{
    typedef ip::tcp::acceptor   acceptor_type;
    typedef ip::tcp::endpoint   endpoint_type;
    typedef ip::tcp::socket     socket_type;

    io_service io;

    spawn(io,[&](yield_context yield)
    {
        
        acceptor_type acceptor(io,endpoint(ip::tcp::v4(),6688));
        for(;;)
        {
        socket_type sock(io);
        acceptor.async_accept(sock,yield[ec]);
        if(ec)
        {
            return;
        }
        auto len=sock.async_write_some(
            buffer("hello coroutine"),yield

        );
        cout<<"send"<<len<<"bytes"<<endl;
    })
    


}
io.run();
return 
}

我们可以对比一下同步服务器代码,两者都是在一个for循环里监听端口,但协程版本是在 spawn ()函数里利用协程实现的,接受连接和发送数据使用的则是异步的async_accept ()和 async_write_some ( ),这样在每个异步调用处并不会阻塞,而是转入其他的协程,只有真正发生连接事件和写完成事件时才会继续执行后续的代码。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值