keepalive_options.hpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /*
  2. * Copyright (c) 2017-2023 zhllxt
  3. *
  4. * author : zhllxt
  5. * email : 37792738@qq.com
  6. *
  7. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. */
  10. #ifndef __ASIO2_KEEPALIVE_OPTIONS_HPP__
  11. #define __ASIO2_KEEPALIVE_OPTIONS_HPP__
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. #pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <tuple>
  16. #include <asio2/external/predef.h>
  17. #include <asio2/base/error.hpp>
  18. #include <asio2/base/detail/util.hpp>
  19. #if ASIO2_OS_WINDOWS
  20. # if __has_include(<Mstcpip.h>)
  21. # include <Mstcpip.h> // tcp_keepalive struct
  22. # endif
  23. #endif
  24. namespace asio2::detail
  25. {
  26. /**
  27. * @brief set tcp socket keep alive options
  28. * @param onoff - Turn keepalive on or off.
  29. * @param idle - How many seconds after the connection is idle, start sending keepalives.
  30. * @param interval - How many seconds later to send again when no reply is received.
  31. * @param count - How many times to resend when no reply is received.
  32. * @li on macOS Catalina 10.15.5 (19F101), the default value is:
  33. * onoff - false, idle - 7200, interval - 75, count - 8
  34. */
  35. template<class SocketT>
  36. typename std::enable_if_t<std::is_same_v<typename SocketT::lowest_layer_type::protocol_type, asio::ip::tcp>, bool>
  37. set_keepalive_options(
  38. SocketT& socket,
  39. bool onoff = true,
  40. unsigned int idle = 60,
  41. unsigned int interval = 3,
  42. unsigned int count = 3
  43. ) noexcept
  44. {
  45. if (!socket.is_open())
  46. {
  47. set_last_error(asio::error::not_connected);
  48. return false;
  49. }
  50. error_code ec;
  51. asio::socket_base::keep_alive option(onoff);
  52. socket.set_option(option, ec);
  53. if (ec)
  54. {
  55. set_last_error(ec);
  56. return false;
  57. }
  58. auto native_fd = socket.native_handle();
  59. detail::ignore_unused(onoff, idle, interval, count, native_fd);
  60. #if ASIO2_OS_LINUX
  61. // For *n*x systems
  62. int ret_keepidle = setsockopt(native_fd, SOL_TCP, TCP_KEEPIDLE , (void*)&idle , sizeof(unsigned int));
  63. if (ret_keepidle)
  64. {
  65. set_last_error(errno, asio::error::get_system_category());
  66. return false;
  67. }
  68. int ret_keepintvl = setsockopt(native_fd, SOL_TCP, TCP_KEEPINTVL, (void*)&interval, sizeof(unsigned int));
  69. if (ret_keepintvl)
  70. {
  71. set_last_error(errno, asio::error::get_system_category());
  72. return false;
  73. }
  74. int ret_keepcount = setsockopt(native_fd, SOL_TCP, TCP_KEEPCNT , (void*)&count , sizeof(unsigned int));
  75. if (ret_keepcount)
  76. {
  77. set_last_error(errno, asio::error::get_system_category());
  78. return false;
  79. }
  80. #elif ASIO2_OS_UNIX
  81. // be pending
  82. #elif ASIO2_OS_MACOS
  83. int ret_keepalive = setsockopt(native_fd, IPPROTO_TCP, TCP_KEEPALIVE, (void*)&idle , sizeof(unsigned int));
  84. if (ret_keepalive)
  85. {
  86. set_last_error(errno, asio::error::get_system_category());
  87. return false;
  88. }
  89. int ret_keepintvl = setsockopt(native_fd, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&interval, sizeof(unsigned int));
  90. if (ret_keepintvl)
  91. {
  92. set_last_error(errno, asio::error::get_system_category());
  93. return false;
  94. }
  95. int ret_keepcount = setsockopt(native_fd, IPPROTO_TCP, TCP_KEEPCNT , (void*)&count , sizeof(unsigned int));
  96. if (ret_keepcount)
  97. {
  98. set_last_error(errno, asio::error::get_system_category());
  99. return false;
  100. }
  101. #elif ASIO2_OS_IOS
  102. // be pending
  103. #elif ASIO2_OS_WINDOWS
  104. // on WSL ubuntu, the ASIO2_OS_WINDOWS is true, but can't find the <Mstcpip.h> file.
  105. #if __has_include(<Mstcpip.h>)
  106. // Partially supported on windows
  107. tcp_keepalive keepalive_options;
  108. keepalive_options.onoff = onoff;
  109. keepalive_options.keepalivetime = idle * 1000; // Keep Alive in milliseconds.
  110. keepalive_options.keepaliveinterval = interval * 1000; // Resend if No-Reply
  111. DWORD bytes_returned = 0;
  112. if (SOCKET_ERROR == ::WSAIoctl(native_fd, SIO_KEEPALIVE_VALS, (LPVOID)&keepalive_options,
  113. (DWORD)sizeof(keepalive_options), nullptr, 0, (LPDWORD)&bytes_returned, nullptr, nullptr))
  114. {
  115. if (::WSAGetLastError() != WSAEWOULDBLOCK)
  116. {
  117. set_last_error(::WSAGetLastError(), asio::error::get_system_category());
  118. return false;
  119. }
  120. }
  121. #endif
  122. #endif
  123. return true;
  124. }
  125. template<class SocketT>
  126. typename std::enable_if_t<!std::is_same_v<typename SocketT::lowest_layer_type::protocol_type, asio::ip::tcp>, bool>
  127. set_keepalive_options(
  128. SocketT& socket,
  129. bool onoff = true,
  130. unsigned int idle = 60,
  131. unsigned int interval = 3,
  132. unsigned int count = 3
  133. ) noexcept
  134. {
  135. detail::ignore_unused(socket, onoff, idle, interval, count);
  136. return true;
  137. }
  138. }
  139. #endif // !__ASIO2_KEEPALIVE_OPTIONS_HPP__