目录
1. 示例 32.8 使用 boost::asio::windows::object_handle
2 示例 32.9: 使用 boost::asio::windows::overlapped_ptr
3 示例 32.10。使用 boost::asio::posix::stream_descriptor
一、前言
特定于平台的 I/O 对象到目前为止,本章中的所有示例都是平台无关的。所有平台都支持 I/O 对象,例如 boost::asio::steady_timer 和 boost::asio::ip::tcp::socket。但是,Boost.Asio 还提供了特定于平台的 I/O 对象,因为某些异步操作仅在某些平台上可用,例如 Windows 或 Linux。
二、平台相关IO示例
1. 示例 32.8 使用 boost::asio::windows::object_handle
#include <boost/asio/io_service.hpp>
#include <boost/asio/windows/object_handle.hpp>
#include <boost/system/error_code.hpp>
#include <iostream>
#include <Windows.h>
using namespace boost::asio;
using namespace boost::system;
int main()
{
io_service ioservice;
HANDLE file_handle = CreateFileA(".", FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
char buffer[1024];
DWORD transferred;
OVERLAPPED overlapped;
ZeroMemory(&overlapped, sizeof(overlapped));
overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
ReadDirectoryChangesW(file_handle, buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME, &transferred, &overlapped, NULL);
windows::object_handle obj_handle{ioservice, overlapped.hEvent};
obj_handle.async_wait([&buffer, &overlapped](const error_code &ec) {
if (!ec)
{
DWORD transferred;
GetOverlappedResult(overlapped.hEvent, &overlapped, &transferred,
FALSE);
auto notification = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(buffer);
std::wcout << notification->Action << '\n';
std::streamsize size = notification->FileNameLength / sizeof(wchar_t);
std::wcout.write(notification->FileName, size);
}
});
ioservice.run();
}
示例 32.8 使用 I/O 对象 boost::asio::windows::object_handle,它仅在 Windows 上可用。 boost::asio::windows::object_handle 基于 Windows 函数 RegisterWaitForSingleObject(),可让您启动对象句柄的异步操作。 RegisterWaitForSingleObject() 接受的所有句柄都可以与 boost::asio::windows::object_handle 一起使用。使用 async_wait(),可以异步等待对象句柄更改。
示例 32.8 使用使用 Windows 函数 CreateEvent() 创建的对象句柄初始化类型为 boost::asio::windows::object_handle 的对象 obj_handle。句柄是 OVERLAPPED 结构的一部分,其地址被传递给 Windows 函数 ReadDirectoryChangesW()。 Windows 使用 OVERLAPPED 结构来启动异步操作。
ReadDirectoryChangesW() 可用于监视目录并等待更改。要异步调用函数,必须将 OVERLAPPED 结构传递给 ReadDirectoryChangesW()。为了通过 Boost.Asio 报告异步操作的完成,事件处理程序在传递给 ReadDirectoryChangesW() 之前存储在 OVERLAPPED 结构中。此事件处理程序随后传递给 obj_handle。在 obj_handle 上调用 async_wait() 时,会在观察到的目录中检测到更改时执行处理程序。
运行示例 32.8 时,在您将运行示例的目录中创建一个新文件。程序将检测新文件并将消息写入标准输出流。
2 示例 32.9: 使用 boost::asio::windows::overlapped_ptr
#include <boost/asio/io_service.hpp>
#include <boost/asio/windows/overlapped_ptr.hpp>
#include <boost/system/error_code.hpp>
#include <iostream>
#include <Windows.h>
using namespace boost::asio;
using namespace boost::system;
int main()
{
io_service ioservice;
HANDLE file_handle = CreateFileA(".", FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
error_code ec;
auto &io_service_impl = use_service<detail::io_service_impl>(ioservice);
io_service_impl.register_handle(file_handle, ec);
char buffer[1024];
auto handler = [&buffer](const error_code &ec, std::size_t) {
if (!ec)
{
auto notification =
reinterpret_cast<FILE_NOTIFY_INFORMATION*>(buffer);
std::wcout << notification->Action << '\n';
std::streamsize size = notification->FileNameLength / sizeof(wchar_t);
std::wcout.write(notification->FileName, size);
}
};
windows::overlapped_ptr overlapped{ioservice, handler};
DWORD transferred;
BOOL ok = ReadDirectoryChangesW(file_handle, buffer, sizeof(buffer),
FALSE, FILE_NOTIFY_CHANGE_FILE_NAME, &transferred, overlapped.get(),
NULL);
int last_error = GetLastError();
if (!ok && last_error != ERROR_IO_PENDING)
{
error_code ec{last_error, error::get_system_category()};
overlapped.complete(ec, 0);
}
else
{
overlapped.release();
}
ioservice.run();
}
示例 32.9 使用 ReadDirectoryChangesW() 和上一个一样来监控目录。这一次,对 ReadDirectoryChangesW() 的异步调用未链接到事件句柄。该示例使用类 boost::asio::windows::overlapped_ptr,它在内部使用 OVERLAPPED 结构。 get() 检索指向内部 OVERLAPPED 结构的指针。在示例中,指针随后被传递给 ReadDirectoryChangesW()。
boost::asio::windows::overlapped_ptr 是一个 I/O 对象,它没有用于启动异步操作的成员函数。通过将指向内部 OVERLAPPED 变量的指针传递给 Windows 函数来启动异步操作。除了 I/O 服务对象之外,boost::asio::windows::overlapped_ptr 的构造函数还需要一个处理程序,该处理程序将在异步操作完成时被调用。
示例 32.9 使用 boost::asio::use_service() 来获取对 I/O 服务对象 ioservice 中服务的引用。 boost::asio::use_service() 是一个函数模板。您要获取的 I/O 服务的类型必须作为模板参数传递。在示例中,传递了 boost::asio::detail::io_service_impl。这种类型的 I/O 服务最接近操作系统。在 Windows 上,boost::asio::detail::io_service_impl 使用 IOCP,在 Linux 上使用 epoll()。 boost::asio::detail::io_service_impl 是一种类型定义,在 Windows 上设置为 boost::asio::detail::win_iocp_io_service,在 Linux 上设置为 boost::asio::detail::task_io_service。
boost::asio::detail::win_iocp_io_service 提供成员函数 register_handle() 将句柄链接到 IOCP 句柄。 register_handle() 调用 Windows 函数 CreateIoCompletionPort()。此调用是示例正常工作所必需的。 CreateFileA() 返回的句柄只有在链接到 IOCP 句柄后才能通过重叠传递到 ReadDirectoryChangesW()。
示例 32.9 检查 ReadDirectoryChangesW() 是否失败。如果 ReadDirectoryChangesW() 失败,则会重叠调用 complete() 以完成 Boost.Asio 的异步操作。传递给 complete() 的参数将转发给处理程序。
如果 ReadDirectoryChangesW() 成功,则调用 release()。然后异步操作处于挂起状态,仅在使用 Windows 函数 ReadDirectoryChangesW() 启动的操作完成后才会完成。
3 示例 32.10。使用 boost::asio::posix::stream_descriptor
#include <boost/asio/io_service.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/asio/write.hpp>
#include <boost/system/error_code.hpp>
#include <iostream>
#include <unistd.h>
using namespace boost::asio;
int main()
{
io_service ioservice;
posix::stream_descriptor stream{ioservice, STDOUT_FILENO};
auto handler = [](const boost::system::error_code&, std::size_t) {
std::cout << ", world!\n";
};
async_write(stream, buffer("Hello"), handler);
ioservice.run();
}
示例 32.10 为 POSIX 平台引入了一个 I/O 对象。
boost::asio::posix::stream_descriptor 可以使用文件描述符初始化,以对该文件描述符启动异步操作。在示例中,流链接到文件描述符 STDOUT_FILENO 以将字符串异步写入标准输出流。