vmci_connecter.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
  3. This file is part of libzmq, the ZeroMQ core engine in C++.
  4. libzmq is free software; you can redistribute it and/or modify it under
  5. the terms of the GNU Lesser General Public License (LGPL) as published
  6. by the Free Software Foundation; either version 3 of the License, or
  7. (at your option) any later version.
  8. As a special exception, the Contributors give you permission to link
  9. this library with independent modules to produce an executable,
  10. regardless of the license terms of these independent modules, and to
  11. copy and distribute the resulting executable under terms of your choice,
  12. provided that you also meet, for each linked independent module, the
  13. terms and conditions of the license of that module. An independent
  14. module is a module which is not derived from or based on this library.
  15. If you modify this library, you must extend this exception to your
  16. version of the library.
  17. libzmq is distributed in the hope that it will be useful, but WITHOUT
  18. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  19. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  20. License for more details.
  21. You should have received a copy of the GNU Lesser General Public License
  22. along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. #include "precompiled.hpp"
  25. #include "vmci_connecter.hpp"
  26. #if defined ZMQ_HAVE_VMCI
  27. #include <new>
  28. #include "stream_engine.hpp"
  29. #include "io_thread.hpp"
  30. #include "platform.hpp"
  31. #include "random.hpp"
  32. #include "err.hpp"
  33. #include "ip.hpp"
  34. #include "address.hpp"
  35. #include "session_base.hpp"
  36. #include "vmci_address.hpp"
  37. #include "vmci.hpp"
  38. zmq::vmci_connecter_t::vmci_connecter_t (class io_thread_t *io_thread_,
  39. class session_base_t *session_,
  40. const options_t &options_,
  41. const address_t *addr_,
  42. bool delayed_start_) :
  43. own_t (io_thread_, options_),
  44. io_object_t (io_thread_),
  45. addr (addr_),
  46. s (retired_fd),
  47. handle_valid (false),
  48. delayed_start (delayed_start_),
  49. timer_started (false),
  50. session (session_),
  51. current_reconnect_ivl (options.reconnect_ivl)
  52. {
  53. zmq_assert (addr);
  54. zmq_assert (addr->protocol == "vmci");
  55. addr->to_string (endpoint);
  56. socket = session->get_socket ();
  57. }
  58. zmq::vmci_connecter_t::~vmci_connecter_t ()
  59. {
  60. zmq_assert (!timer_started);
  61. zmq_assert (!handle_valid);
  62. zmq_assert (s == retired_fd);
  63. }
  64. void zmq::vmci_connecter_t::process_plug ()
  65. {
  66. if (delayed_start)
  67. add_reconnect_timer ();
  68. else
  69. start_connecting ();
  70. }
  71. void zmq::vmci_connecter_t::process_term (int linger_)
  72. {
  73. if (timer_started) {
  74. cancel_timer (reconnect_timer_id);
  75. timer_started = false;
  76. }
  77. if (handle_valid) {
  78. rm_fd (handle);
  79. handle_valid = false;
  80. }
  81. if (s != retired_fd)
  82. close ();
  83. own_t::process_term (linger_);
  84. }
  85. void zmq::vmci_connecter_t::in_event ()
  86. {
  87. // We are not polling for incoming data, so we are actually called
  88. // because of error here. However, we can get error on out event as well
  89. // on some platforms, so we'll simply handle both events in the same way.
  90. out_event ();
  91. }
  92. void zmq::vmci_connecter_t::out_event ()
  93. {
  94. fd_t fd = connect ();
  95. rm_fd (handle);
  96. handle_valid = false;
  97. // Handle the error condition by attempt to reconnect.
  98. if (fd == retired_fd) {
  99. close ();
  100. add_reconnect_timer ();
  101. return;
  102. }
  103. tune_vmci_buffer_size (this->get_ctx (), fd, options.vmci_buffer_size,
  104. options.vmci_buffer_min_size,
  105. options.vmci_buffer_max_size);
  106. if (options.vmci_connect_timeout > 0) {
  107. #if defined ZMQ_HAVE_WINDOWS
  108. tune_vmci_connect_timeout (this->get_ctx (), fd,
  109. options.vmci_connect_timeout);
  110. #else
  111. struct timeval timeout = {0, options.vmci_connect_timeout * 1000};
  112. tune_vmci_connect_timeout (this->get_ctx (), fd, timeout);
  113. #endif
  114. }
  115. // Create the engine object for this connection.
  116. stream_engine_t *engine = new (std::nothrow) stream_engine_t (
  117. fd, options, make_unconnected_bind_endpoint_pair (endpoint));
  118. alloc_assert (engine);
  119. // Attach the engine to the corresponding session object.
  120. send_attach (session, engine);
  121. // Shut the connecter down.
  122. terminate ();
  123. socket->event_connected (make_unconnected_bind_endpoint_pair (endpoint),
  124. fd);
  125. }
  126. void zmq::vmci_connecter_t::timer_event (int id_)
  127. {
  128. zmq_assert (id_ == reconnect_timer_id);
  129. timer_started = false;
  130. start_connecting ();
  131. }
  132. void zmq::vmci_connecter_t::start_connecting ()
  133. {
  134. // Open the connecting socket.
  135. int rc = open ();
  136. // Connect may succeed in synchronous manner.
  137. if (rc == 0) {
  138. handle = add_fd (s);
  139. handle_valid = true;
  140. out_event ();
  141. }
  142. // Handle any other error condition by eventual reconnect.
  143. else {
  144. if (s != retired_fd)
  145. close ();
  146. add_reconnect_timer ();
  147. }
  148. }
  149. void zmq::vmci_connecter_t::add_reconnect_timer ()
  150. {
  151. if (options.reconnect_ivl > 0) {
  152. int rc_ivl = get_new_reconnect_ivl ();
  153. add_timer (rc_ivl, reconnect_timer_id);
  154. socket->event_connect_retried (
  155. make_unconnected_bind_endpoint_pair (endpoint), rc_ivl);
  156. timer_started = true;
  157. }
  158. }
  159. int zmq::vmci_connecter_t::get_new_reconnect_ivl ()
  160. {
  161. // The new interval is the current interval + random value.
  162. int this_interval =
  163. current_reconnect_ivl + (generate_random () % options.reconnect_ivl);
  164. // Only change the current reconnect interval if the maximum reconnect
  165. // interval was set and if it's larger than the reconnect interval.
  166. if (options.reconnect_ivl_max > 0
  167. && options.reconnect_ivl_max > options.reconnect_ivl) {
  168. // Calculate the next interval
  169. current_reconnect_ivl = current_reconnect_ivl * 2;
  170. if (current_reconnect_ivl >= options.reconnect_ivl_max) {
  171. current_reconnect_ivl = options.reconnect_ivl_max;
  172. }
  173. }
  174. return this_interval;
  175. }
  176. int zmq::vmci_connecter_t::open ()
  177. {
  178. zmq_assert (s == retired_fd);
  179. int family = this->get_ctx ()->get_vmci_socket_family ();
  180. if (family == -1)
  181. return -1;
  182. // Create the socket.
  183. s = open_socket (family, SOCK_STREAM, 0);
  184. #ifdef ZMQ_HAVE_WINDOWS
  185. if (s == INVALID_SOCKET) {
  186. errno = wsa_error_to_errno (WSAGetLastError ());
  187. return -1;
  188. }
  189. #else
  190. if (s == -1)
  191. return -1;
  192. #endif
  193. // Connect to the remote peer.
  194. int rc = ::connect (s, addr->resolved.vmci_addr->addr (),
  195. addr->resolved.vmci_addr->addrlen ());
  196. // Connect was successful immediately.
  197. if (rc == 0)
  198. return 0;
  199. // Forward the error.
  200. return -1;
  201. }
  202. void zmq::vmci_connecter_t::close ()
  203. {
  204. zmq_assert (s != retired_fd);
  205. #ifdef ZMQ_HAVE_WINDOWS
  206. const int rc = closesocket (s);
  207. wsa_assert (rc != SOCKET_ERROR);
  208. #else
  209. const int rc = ::close (s);
  210. errno_assert (rc == 0);
  211. #endif
  212. socket->event_closed (make_unconnected_bind_endpoint_pair (endpoint), s);
  213. s = retired_fd;
  214. }
  215. zmq::fd_t zmq::vmci_connecter_t::connect ()
  216. {
  217. // Following code should handle both Berkeley-derived socket
  218. // implementations and Solaris.
  219. int err = 0;
  220. #if defined ZMQ_HAVE_HPUX
  221. int len = sizeof (err);
  222. #else
  223. socklen_t len = sizeof (err);
  224. #endif
  225. int rc = getsockopt (s, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
  226. // Assert if the error was caused by 0MQ bug.
  227. // Networking problems are OK. No need to assert.
  228. #ifdef ZMQ_HAVE_WINDOWS
  229. zmq_assert (rc == 0);
  230. if (err != 0) {
  231. if (err != WSAECONNREFUSED && err != WSAETIMEDOUT
  232. && err != WSAECONNABORTED && err != WSAEHOSTUNREACH
  233. && err != WSAENETUNREACH && err != WSAENETDOWN && err != WSAEACCES
  234. && err != WSAEINVAL && err != WSAEADDRINUSE
  235. && err != WSAECONNRESET) {
  236. wsa_assert_no (err);
  237. }
  238. return retired_fd;
  239. }
  240. #else
  241. // Following code should handle both Berkeley-derived socket
  242. // implementations and Solaris.
  243. if (rc == -1)
  244. err = errno;
  245. if (err != 0) {
  246. errno = err;
  247. errno_assert (errno == ECONNREFUSED || errno == ECONNRESET
  248. || errno == ETIMEDOUT || errno == EHOSTUNREACH
  249. || errno == ENETUNREACH || errno == ENETDOWN
  250. || errno == EINVAL);
  251. return retired_fd;
  252. }
  253. #endif
  254. fd_t result = s;
  255. s = retired_fd;
  256. return result;
  257. }
  258. #endif