输入/输出 IO 抽象
当 Sandwich 库需要传输数据流时,它通过通用的 I/O 接口实现。换句话说,Sandwich 不会强制隧道对等体之间数据传输的方式。
该 I/O 接口提供了三个由用户定义的高级别 API 调用:
read:读取底层传输的指定字节数 write:将缓冲区写入底层传输 它还包含一个通用对象的视图,可以表示实际传输所需的任何状态。I/O 对象始终由隧道拥有,使 I/O API 能够访问其父隧道,例如获取隧道的当前状态。
I/O 接口还支持异步操作,并且可以为此类目的返回特定的错误代码。
C API
I/O 接口通过 SandwichCIOSettings 结构在 C API 中进行描述。
以下是在 C++ 中将数据转发到套接字的 I/O 结构的示例:
/// \brief Read from a socket.
///
/// This method is a SandwichCIOReadFunction.
auto SandwichReadFromSocket(
void *uarg, void *buf, const size_t count,
[[maybe_unused]] const enum ::SandwichTunnelState state,
enum ::SandwichIOError *err) -> size_t {
*err = SANDWICH_IOERROR_OK;
const auto fd = static_cast<int>(reinterpret_cast<uintptr_t>(uarg));
ssize_t r{0};
do {
if (r = ::read(fd, buf, count); r > -1) {
return static_cast<size_t>(r);
}
} while ((r == -1) && (errno == EINTR));
switch (errno) {
case 0: {
return *err = SANDWICH_IOERROR_OK, 0;
}
case EINPROGRESS:
case EINTR: {
return *err = SANDWICH_IOERROR_IN_PROGRESS, 0;
}
case EWOULDBLOCK:
#if EWOULDBLOCK != EAGAIN
case EAGAIN:
#endif
{
return *err = SANDWICH_IOERROR_WOULD_BLOCK, 0;
}
case ENOTSOCK:
case EPROTOTYPE:
case EBADF: {
return *err = SANDWICH_IOERROR_INVALID, 0;
}
case EACCES:
case EPERM:
case ETIMEDOUT:
case ENETUNREACH:
case ECONNREFUSED: {
return *err = SANDWICH_IOERROR_REFUSED, 0;
}
default: {
return *err = SANDWICH_IOERROR_UNKNOWN, 0;
}
}
}
/// \brief Write to a socket.
///
/// This method is a SandwichCIOWriteFunction.
auto SandwichWriteToSocket(
void *uarg, const void *buf, const size_t count,
[[maybe_unused]] const enum ::SandwichTunnelState state,
enum ::SandwichIOError *err) -> size_t {
*err = SANDWICH_IOERROR_OK;
const auto fd = static_cast<int>(reinterpret_cast<uintptr_t>(uarg));
ssize_t w{0};
do {
if (w = ::write(fd, buf, count); w > -1) {
return static_cast<size_t>(w);
}
} while ((w == -1) && (errno == EINTR));
switch (errno) {
case 0: {
return *err = SANDWICH_IOERROR_OK, 0;
}
case EINPROGRESS:
case EINTR: {
return *err = SANDWICH_IOERROR_WOULD_BLOCK, 0;
}
case ENOTSOCK:
case EPROTOTYPE:
case EBADF: {
return *err = SANDWICH_IOERROR_INVALID, 0;
}
case EACCES:
case EPERM:
case ETIMEDOUT:
case ENETUNREACH:
case ECONNREFUSED: {
return *err = SANDWICH_IOERROR_REFUSED, 0;
}
default: {
return *err = SANDWICH_IOERROR_UNKNOWN, 0;
}
}
}
/// \brief Close a socket.
///
/// This method is a SandwichCIOCloseFunction.
void CloseSocket(void *uarg) {
const auto fd = static_cast<int>(reinterpret_cast<uintptr_t>(uarg));
::close(fd);
}
/// \brief Global CIO settings structure for sockets.
constexpr struct ::SandwichCIOSettings SandwichSocketCIOSettings = {
.read = SandwichReadFromSocket,
.write = SandwichWriteToSocket,
.uarg = nullptr};