​原文​​​​asio​​的异步接口能连续调用吗?比如可以这样调用异步写接口吗?

向量<>消息{"你好","发送许多"};
(&消息:消息){
异网::异步写(套接字_,
异网::缓冲(消息.数据(),消息.长度()),
[](错误码 ec,大小型 长度){
(ec)关闭();
})
}

​asio​​​不允许​​连续​​​调用​​异步​​​接口,必须在上次​​调用​​​完成之后才能再调用异步接口.
如何​​​连续发送​​​消息呢?
一个​​​简单​​​思路是​​client​​​内部搞一个​​发送队列​​​,用户​​连续​​​发送数据丢到​​队列​​​中,然后再​​异步​​​发送队列中的数据.​​chat_client​​的例子中展示了如何使用队列实现连续发送消息:

 ( &消息)
{
正在写=!写消息_.空的();
写消息_.压后(消息);
(!正在写)
{
干写();
}
}

干写()
{
异网::异步写(套接字_,
异网::缓冲(写消息_.().数据(),
写消息_.().长度()),
[](错误码 ec,大小型/*长度*/)
{
(!ec)
{
写消息_.弹前();
(!写消息_.空的())
{
干写();
}
}

{
套接字_.关闭();
}
});
}

双队<>写消息_;

​干写​​​展示了​​异步调用链​​​,每次​​异步​​​写时从​​队列​​​头拿​​数据​​​去发送,发送完成​​回调​​​里移除​​头部​​​已发送完成数据,接着再​​检查​​​队列中是否​​还有数据​​​,如果有就继续调用​​异步发送​​接口发送,直到队列为空.

这里一个​​小问题​​​需要注意,队列中有​​很多数据​​​时,正在​​递归​​​异步​​发送​​​数据时,如何​​保证​​​外部用户不会​​调用​​​异步写接口呢?
外面用户不知道是否已发送完成​​​内部队列数据​​​,如果这时用户在外面调用了​​异步写​​​接口,那就相当于连续调用了​​异步接口​​​,产生​​未定义行为​​​(两次缓冲区交叠).这时应在​​用户​​​发送接口中判断​​队列​​​是否为空,如果​​不为空​​​就放到​​队列​​​中并返回,如果队列​​为空​​​,就调用​​异步发送数据​​​的接口.所以​​队列​​​是否为空可判断能否调用​​异步写接口​​.

这个​​write_msgs_​​​,还有​​线程安全​​​问题.因为​​用户​​​线程和​​io_context​​​线程都可能访问这个​​write_msgs_​​​.还要修改一下来保证​​线程安全​​.

<io流>
#包含<异网.h++>
#包含<双队>
#包含<串>
#包含<向量>
#包含<互斥锁>

异网::ip::传控;

聊天客户
{
:
聊天客户(异网::io环境&io环境,
传控::解析器::结果类型&端点)
:io环境_(io环境),
套接字_(io环境)
{
干连接(端点);
}

( &消息)
{
独锁 (写互斥锁_);
正在写=!写消息_.空的();
写消息_.压后(消息);
(!正在写)
{
干写();
}
}

关闭()
{
异网::提交(io环境_,[](){套接字_.关闭();});
}

跑一(){
io环境_.跑一();
}

:
干连接( 传控::解析器::结果类型&端点)
{
异网::异步连接(套接字_,端点,[](错误码 ec,传控::端点){
(!ec)干读();
});
}

干读()
{
套接字_.异步读些(异网::缓冲(读消息_,512),
[](错误码 ec,大小型 长度)
{
(!ec)
{
输出<<"接收消息:"<<(读消息_,长度)<<"\n";
干读();
}

{
套接字_.关闭();
}
});
}

干写()
{
异网::异步写(套接字_,
异网::缓冲(写消息_.().数据(),
写消息_.().长度()),
[](错误码 ec,大小型/*长度*/)
{
(!ec)
{
独锁 (写互斥锁_);
写消息_.弹前();
(!写消息_.空的())
{
干写();
}
}

{
套接字_.关闭();
}
});
}

:
异网::io环境&io环境_;
传控::套接字 套接字_;
读消息_[512];
双队<>写消息_;
互斥锁 写互斥锁_;//用互斥锁来锁定.
};

( 参个数,*参值[])
{

{
(参个数!=3)
{
c错误<<"使用:聊天客户<主机><端口>\n";
1;
}

异网::io环境 io环境;

传控::解析器 解析器(io环境);
端点=解析器.解析(参值[1],参值[2]);
聊天客户 c(io环境,端点);
c.跑一();

线程 t([&io环境](){io环境.();});

向量<>向量{"你好","发送许多"};
(&消息:向量){
c.(消息);
}

t.合并();
io环境.();
}
(异常&e)
{
c错误<<"异常:"<<e.什么()<<"\n";
}

0;
}

​asio​​​提供了​​strand​​​,​​strand​​​会保证并发调用​​异步接口​​​的安全性,用​​strand​​就不用锁了,代码会更简单.

 提交客户
{
:
提交客户(异网::io环境&io环境,
传控::解析器::结果类型&端点)
:io环境_(io环境),
套接字_(io环境)
{
干连接(端点);
}

( 消息)
{
异网::提交(io环境_,
[,消息=移动(消息)]()
{
正在写=!写消息_.空的();
写消息_.压后(移动(消息));
(!正在写)
{
干写();
}
});
}

关闭()
{
异网::提交(io环境_,[](){套接字_.关闭();});
}

跑一(){
io环境_.跑一();
}

:
干连接( 传控::解析器::结果类型&端点)
{
异网::异步连接(套接字_,端点,
[](错误码 ec,传控::端点)
{
(!ec)
{
输出<<"连接好\n";
干读();
}
});
}

干读()
{
套接字_.异步读些(异网::缓冲(读消息_,512),
[](错误码 ec,大小型 长度)
{
(!ec)
{
输出<<"接收消息:"<<(读消息_,长度)<<"\n";
干读();
}

{
关闭();
}
});
}

干写()
{
异网::异步写(套接字_,
异网::缓冲(写消息_.().数据(),
写消息_.().长度()),
[](错误码 ec,大小型/*长度*/)
{
(!ec)
{
写消息_.弹前();
(!写消息_.空的())
{
干写();
}
}

{
关闭();
}
});
}

:
关闭()
{
异网::提交(io环境_,[]{
(!套接字_.是打开()){
;
}
异网::错误码 忽略误码;
套接字_.关闭(传控::套接字::都关闭,忽略误码);
套接字_.关闭(忽略误码);
});
}
异网::io环境&io环境_;
传控::套接字 套接字_;
读消息_[512];
双队<>写消息_;
};