说到异步, 即不用新开线程, 可以在同一个线程里使用. 而异步即是适时执行回调而已. 这时候什么时候执行回调,而且还要在同一个线程调用就至关重要了. 那boost如何能做到有消息到来就可以通知到接收函数这一点的呢? 这里的原理就在于其”通知”功能其实是使用的循环检测. 怎么说呢? 处理网络数据在其它进程里面, 分发到对应线程里面. 不过这个操作是阻塞操作. 不过不在用户自己的线程中就不会影响到用户线程. boost.asio使用boost::asio::io_service负责异步处理网络数据的读取与分发.
io_service与网络通信的线程间应该共享了个消息队列之类的东西, 通信线程负责放东西, io_service负责读东西 下面两个是io的实际执行函数
io_service::run() 阻塞调用, 有数据要发送立即发出去不等待发送完成, 取不到数据就等在那里
io_service::poll() 发数据一样, 不过取不到数据立即返回
(注:以上是我直观的理解,没有科学根据)
这样的话io_service::poll()需要被放到游戏循环的update或者窗口消息处理的MessageBump里面去 而run的版本, 如果有需要把网络通信放到另外一个线程, 而又不想自己update, 就直接使用run吧 剩下的内容就是读写网络数据, boost::asio设计得非常巧妙的一点是, 使用下面的方式:
void XXXClass::read(std::shared_ptr socket) {
auto buf = std::make_shared<vector>();
buf->resize(256);
socket->async_read_some(asio::buffer(*buf), [this, socket, buf](system::error_code code, size_t len) {
if (code.value()) {
this->errHandler(code, socket);
return;
}
string str;
str.assign(buf->begin(), buf->begin() + len);
this->notifyReaders(RecvStatusCode::OK, str);
this->read(socket); // 注意这里
});
}
拿socket进行端对端的通信 在当前接收的回调函数执行时, 注册下一个读取的回调, 效果即是实现了不停地读取同一个socket也就是同一个端(客户端/服务端)
之后还需要注意就是内存管理问题, 网络数据缓冲区必须使用堆分配, 因为不等待接收/发送完成 socket也需要被new出来, 在前后两次通信中需要不断被传递. 当错误发生时, 需要干掉socket。
|