输入/输出 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 结构的示例:

  1. /// \brief Read from a socket.
  2. ///
  3. /// This method is a SandwichCIOReadFunction.
  4. auto SandwichReadFromSocket(
  5. void *uarg, void *buf, const size_t count,
  6. [[maybe_unused]] const enum ::SandwichTunnelState state,
  7. enum ::SandwichIOError *err) -> size_t {
  8. *err = SANDWICH_IOERROR_OK;
  9. const auto fd = static_cast<int>(reinterpret_cast<uintptr_t>(uarg));
  10. ssize_t r{0};
  11. do {
  12. if (r = ::read(fd, buf, count); r > -1) {
  13. return static_cast<size_t>(r);
  14. }
  15. } while ((r == -1) && (errno == EINTR));
  16. switch (errno) {
  17. case 0: {
  18. return *err = SANDWICH_IOERROR_OK, 0;
  19. }
  20. case EINPROGRESS:
  21. case EINTR: {
  22. return *err = SANDWICH_IOERROR_IN_PROGRESS, 0;
  23. }
  24. case EWOULDBLOCK:
  25. #if EWOULDBLOCK != EAGAIN
  26. case EAGAIN:
  27. #endif
  28. {
  29. return *err = SANDWICH_IOERROR_WOULD_BLOCK, 0;
  30. }
  31. case ENOTSOCK:
  32. case EPROTOTYPE:
  33. case EBADF: {
  34. return *err = SANDWICH_IOERROR_INVALID, 0;
  35. }
  36. case EACCES:
  37. case EPERM:
  38. case ETIMEDOUT:
  39. case ENETUNREACH:
  40. case ECONNREFUSED: {
  41. return *err = SANDWICH_IOERROR_REFUSED, 0;
  42. }
  43. default: {
  44. return *err = SANDWICH_IOERROR_UNKNOWN, 0;
  45. }
  46. }
  47. }
  48. /// \brief Write to a socket.
  49. ///
  50. /// This method is a SandwichCIOWriteFunction.
  51. auto SandwichWriteToSocket(
  52. void *uarg, const void *buf, const size_t count,
  53. [[maybe_unused]] const enum ::SandwichTunnelState state,
  54. enum ::SandwichIOError *err) -> size_t {
  55. *err = SANDWICH_IOERROR_OK;
  56. const auto fd = static_cast<int>(reinterpret_cast<uintptr_t>(uarg));
  57. ssize_t w{0};
  58. do {
  59. if (w = ::write(fd, buf, count); w > -1) {
  60. return static_cast<size_t>(w);
  61. }
  62. } while ((w == -1) && (errno == EINTR));
  63. switch (errno) {
  64. case 0: {
  65. return *err = SANDWICH_IOERROR_OK, 0;
  66. }
  67. case EINPROGRESS:
  68. case EINTR: {
  69. return *err = SANDWICH_IOERROR_WOULD_BLOCK, 0;
  70. }
  71. case ENOTSOCK:
  72. case EPROTOTYPE:
  73. case EBADF: {
  74. return *err = SANDWICH_IOERROR_INVALID, 0;
  75. }
  76. case EACCES:
  77. case EPERM:
  78. case ETIMEDOUT:
  79. case ENETUNREACH:
  80. case ECONNREFUSED: {
  81. return *err = SANDWICH_IOERROR_REFUSED, 0;
  82. }
  83. default: {
  84. return *err = SANDWICH_IOERROR_UNKNOWN, 0;
  85. }
  86. }
  87. }
  88. /// \brief Close a socket.
  89. ///
  90. /// This method is a SandwichCIOCloseFunction.
  91. void CloseSocket(void *uarg) {
  92. const auto fd = static_cast<int>(reinterpret_cast<uintptr_t>(uarg));
  93. ::close(fd);
  94. }
  95. /// \brief Global CIO settings structure for sockets.
  96. constexpr struct ::SandwichCIOSettings SandwichSocketCIOSettings = {
  97. .read = SandwichReadFromSocket,
  98. .write = SandwichWriteToSocket,
  99. .uarg = nullptr};