Platform-specific I/O Objects
So far, all of the examples in this chapter have been platform independent. I/O objects such as boost::asio::steady_timer and boost::asio::ip::tcp::socket are supported on all platforms. However, Boost.Asio also provides platform-specific I/O objects because some asynchronous operations are only available on certain platforms, for example, Windows or Linux.
目前为止,本章中的所有示例都是平台无关的。I/O对象,例如boost::asio::steady_timer 和boost::asio::ip::tcp::socket支持所有平台。但是,Boost.Asio也提供了平台相关的I/O对象,因为一些异步操作仅能在特定平台,如Windows或Linux得到。
Example 32.8. Using 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();
}
Example 32.8 uses the I/O object boost::asio::windows::object_handle, which is only available on Windows. boost::asio::windows::object_handle, which is based on the Windows function RegisterWaitForSingleObject(), lets you start asynchronous operations for object handles. All handles accepted by RegisterWaitForSingleObject() can be used with boost::asio::windows::object_handle. With async_wait(), it is possible to wait asynchronously for an object handle to change.
Example 32.8 initializes the object obj_handle of type boost::asio::windows::object_handle with an object handle created with the Windows function CreateEvent(). The handle is part of an OVERLAPPED structure whose address is passed to the Windows function ReadDirectoryChangesW(). Windows uses OVERLAPPED structures to start asynchronous operations.
ReadDirectoryChangesW() can be used to monitor a directory and wait for changes. To call the function asynchronously, an OVERLAPPED structure must be passed to ReadDirectoryChangesW(). To report the completion of the asynchronous operation through Boost.Asio, an event handler is stored in the OVERLAPPED structure before it is passed to ReadDirectoryChangesW(). This event handler is passed to obj_handle afterwards. When async_wait() is called on obj_handle, the handler is executed when a change is detected in the observed directory.
When you run Example 32.8, create a new file in the directory from which you will run the example. The program will detect the new file and write a message to the standard output stream.
示例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有一个boost::asio::windows::object_handle类型的对象obj_handle,该对象采用windows函数CreateEvent()创建的对象句柄初始化而来。这个句柄是OVERLAPPED结构的一部分,该结构的地址是要传递给Windows函数ReadDirectoryChangesW()的,Windows利用OVERLAPPED来开始一个异步操作。
ReadDirectoryChangesW()可以监视一个目录,等待其发生变化,为异步地调用该函数,一个OVERLAPPED结构需要传递给它,为了能通过Boost.Asio来报告异步操作已完成,在OVERLAPPED结构传递给ReadDirectoryChangesW()以前,需要将一个事件句柄保存到OVERLAPPED结构中,然后这个事件句柄传给obj_handle。当obj_handle上的async_wait()被调用,监测的目录发生变化时,这个句柄被执行。
运行示例32.8,你在运行的目录下创建一个文件,这个程序就会检测到这个新文件,然后在标准输出流中产生一条信息。
Example 32.9. Using 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();
}
Example 32.9 uses ReadDirectoryChangesW() like the previous one to monitor a directory. This time, the asynchronous call to ReadDirectoryChangesW() isn’t linked to an event handle. The example uses the class boost::asio::windows::overlapped_ptr, which uses an OVERLAPPED structure internally. get() retrieves a pointer to the internal OVERLAPPED structure. In the example, the pointer is then passed to ReadDirectoryChangesW().
boost::asio::windows::overlapped_ptr is an I/O object that has no member function to start an asynchronous operation. The asynchronous operation is started by passing a pointer to the internal OVERLAPPED variable to a Windows function. In addition to an I/O service object, the constructor of boost::asio::windows::overlapped_ptr expects a handler that will be called when the asynchronous operation completes.
Example 32.9 uses boost::asio::use_service() to get a reference to a service in the I/O service object ioservice. boost::asio::use_service() is a function template. The type of the I/O service you want to fetch has to be passed as a template parameter. In the example, boost::asio::detail::io_service_impl is passed. This type of the I/O service is closest to the operating system. On Windows, boost::asio::detail::io_service_impl uses IOCP, and on Linux it uses epoll(). boost::asio::detail::io_service_impl is a type definition that is set to boost::asio::detail::win_iocp_io_service on Windows and to boost::asio::detail::task_io_service on Linux.
boost::asio::detail::win_iocp_io_service provides the member function register_handle() to link a handle to an IOCP handle. register_handle() calls the Windows function CreateIoCompletionPort(). This call is required for the example to work correctly. The handle returned by CreateFileA() may be passed through overlapped to ReadDirectoryChangesW() only after it is linked to an IOCP handle.
Example 32.9 checks whether ReadDirectoryChangesW() has failed. If ReadDirectoryChangesW() failed, complete() is called on overlapped to complete the asynchronous operation for Boost.Asio. The parameters passed to complete() are forwarded to the handler.
If ReadDirectoryChangesW() succeeds, release() is called. The asynchronous operation is then pending and is only completed after the operation which was initiated with the Windows function ReadDirectoryChangesW() has completed.
示例32.9采用了如上例一样的ReadDirectoryChangesW()来监视目录。这一次,异步调用的ReadDirectoryChangesW()不再连接到一个事件句柄。本例使用了boost::asio::windows::overlapped_ptr类,该类内部使用了一个OVERLAPPED结构,get()函数得到一个指向内部OVERLAPPED结构的指针,这个指针被传递给ReadDirectoryChangesW()。
boost::asio::windows::overlapped_ptr是一个I/O对象,它没有开始异步操作的成员函数,开始一个异步操作是通过将内部OVERLAPPED变量的指针传递给Windows函数来进行的。另外,boost::asio::windows::overlapped_ptr的构造函数还需要一个I/O服务对象和一个异步操作完成时被调用的句柄函数。
示例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()成功,releas()被调用,异步操作被挂起,仅当被Windows函数ReadDirectoryChangesW()触发的操作完成后,异步操作才算完成。
Example 32.10. Using 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();
}
Example 32.10 introduces an I/O object for POSIX platforms.
boost::asio::posix::stream_descriptor can be initialized with a file descriptor to start an asynchronous operation on that file descriptor. In the example, stream is linked to the file descriptor STDOUT_FILENO to write a string asynchronously to the standard output stream.
示例32.10引入了一个POSIX的I/O对象。
boost::asio::posix::stream_descriptor能用一个文件描述符来初始化,并在此文件描述符上开始一个异步操作。示例中,一个流关联到文件描述符STDOUT_FILENO上,并将字符串信息异步地写到标准输出流中。