basic_socket_streambuf.hpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. //
  2. // basic_socket_streambuf.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot 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 ASIO_BASIC_SOCKET_STREAMBUF_HPP
  11. #define ASIO_BASIC_SOCKET_STREAMBUF_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include "asio/detail/push_options.hpp"
  16. #include "asio/detail/push_options.hpp"
  17. #include <boost/config.hpp>
  18. #include "asio/detail/pop_options.hpp"
  19. #if !defined(BOOST_NO_IOSTREAM)
  20. #include "asio/detail/push_options.hpp"
  21. #include <streambuf>
  22. #include <boost/array.hpp>
  23. #include <boost/preprocessor/arithmetic/inc.hpp>
  24. #include <boost/preprocessor/repetition/enum_binary_params.hpp>
  25. #include <boost/preprocessor/repetition/enum_params.hpp>
  26. #include <boost/preprocessor/repetition/repeat_from_to.hpp>
  27. #include <boost/utility/base_from_member.hpp>
  28. #include "asio/detail/pop_options.hpp"
  29. #include "asio/basic_socket.hpp"
  30. #include "asio/io_service.hpp"
  31. #include "asio/stream_socket_service.hpp"
  32. #include "asio/detail/throw_error.hpp"
  33. #if !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY)
  34. #define ASIO_SOCKET_STREAMBUF_MAX_ARITY 5
  35. #endif // !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY)
  36. // A macro that should expand to:
  37. // template <typename T1, ..., typename Tn>
  38. // basic_socket_streambuf<Protocol, StreamSocketService>* connect(
  39. // T1 x1, ..., Tn xn)
  40. // {
  41. // init_buffers();
  42. // asio::error_code ec;
  43. // this->basic_socket<Protocol, StreamSocketService>::close(ec);
  44. // typedef typename Protocol::resolver resolver_type;
  45. // typedef typename resolver_type::query resolver_query;
  46. // resolver_query query(x1, ..., xn);
  47. // resolve_and_connect(query, ec);
  48. // return !ec ? this : 0;
  49. // }
  50. // This macro should only persist within this file.
  51. #define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \
  52. template <BOOST_PP_ENUM_PARAMS(n, typename T)> \
  53. basic_socket_streambuf<Protocol, StreamSocketService>* connect( \
  54. BOOST_PP_ENUM_BINARY_PARAMS(n, T, x)) \
  55. { \
  56. init_buffers(); \
  57. asio::error_code ec; \
  58. this->basic_socket<Protocol, StreamSocketService>::close(ec); \
  59. typedef typename Protocol::resolver resolver_type; \
  60. typedef typename resolver_type::query resolver_query; \
  61. resolver_query query(BOOST_PP_ENUM_PARAMS(n, x)); \
  62. resolve_and_connect(query, ec); \
  63. return !ec ? this : 0; \
  64. } \
  65. /**/
  66. namespace asio {
  67. /// Iostream streambuf for a socket.
  68. template <typename Protocol,
  69. typename StreamSocketService = stream_socket_service<Protocol> >
  70. class basic_socket_streambuf
  71. : public std::streambuf,
  72. private boost::base_from_member<io_service>,
  73. public basic_socket<Protocol, StreamSocketService>
  74. {
  75. public:
  76. /// The endpoint type.
  77. typedef typename Protocol::endpoint endpoint_type;
  78. /// Construct a basic_socket_streambuf without establishing a connection.
  79. basic_socket_streambuf()
  80. : basic_socket<Protocol, StreamSocketService>(
  81. boost::base_from_member<asio::io_service>::member),
  82. unbuffered_(false)
  83. {
  84. init_buffers();
  85. }
  86. /// Destructor flushes buffered data.
  87. virtual ~basic_socket_streambuf()
  88. {
  89. if (pptr() != pbase())
  90. overflow(traits_type::eof());
  91. }
  92. /// Establish a connection.
  93. /**
  94. * This function establishes a connection to the specified endpoint.
  95. *
  96. * @return \c this if a connection was successfully established, a null
  97. * pointer otherwise.
  98. */
  99. basic_socket_streambuf<Protocol, StreamSocketService>* connect(
  100. const endpoint_type& endpoint)
  101. {
  102. init_buffers();
  103. asio::error_code ec;
  104. this->basic_socket<Protocol, StreamSocketService>::close(ec);
  105. this->basic_socket<Protocol, StreamSocketService>::connect(endpoint, ec);
  106. return !ec ? this : 0;
  107. }
  108. #if defined(GENERATING_DOCUMENTATION)
  109. /// Establish a connection.
  110. /**
  111. * This function automatically establishes a connection based on the supplied
  112. * resolver query parameters. The arguments are used to construct a resolver
  113. * query object.
  114. *
  115. * @return \c this if a connection was successfully established, a null
  116. * pointer otherwise.
  117. */
  118. template <typename T1, ..., typename TN>
  119. basic_socket_streambuf<Protocol, StreamSocketService>* connect(
  120. T1 t1, ..., TN tn);
  121. #else
  122. BOOST_PP_REPEAT_FROM_TO(
  123. 1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY),
  124. ASIO_PRIVATE_CONNECT_DEF, _ )
  125. #endif
  126. /// Close the connection.
  127. /**
  128. * @return \c this if a connection was successfully established, a null
  129. * pointer otherwise.
  130. */
  131. basic_socket_streambuf<Protocol, StreamSocketService>* close()
  132. {
  133. asio::error_code ec;
  134. sync();
  135. this->basic_socket<Protocol, StreamSocketService>::close(ec);
  136. if (!ec)
  137. init_buffers();
  138. return !ec ? this : 0;
  139. }
  140. protected:
  141. int_type underflow()
  142. {
  143. if (gptr() == egptr())
  144. {
  145. asio::error_code ec;
  146. std::size_t bytes_transferred = this->service.receive(
  147. this->implementation,
  148. asio::buffer(asio::buffer(get_buffer_) + putback_max),
  149. 0, ec);
  150. if (ec)
  151. return traits_type::eof();
  152. setg(get_buffer_.begin(), get_buffer_.begin() + putback_max,
  153. get_buffer_.begin() + putback_max + bytes_transferred);
  154. return traits_type::to_int_type(*gptr());
  155. }
  156. else
  157. {
  158. return traits_type::eof();
  159. }
  160. }
  161. int_type overflow(int_type c)
  162. {
  163. if (unbuffered_)
  164. {
  165. if (traits_type::eq_int_type(c, traits_type::eof()))
  166. {
  167. // Nothing to do.
  168. return traits_type::not_eof(c);
  169. }
  170. else
  171. {
  172. // Send the single character immediately.
  173. asio::error_code ec;
  174. char_type ch = traits_type::to_char_type(c);
  175. this->service.send(this->implementation,
  176. asio::buffer(&ch, sizeof(char_type)), 0, ec);
  177. if (ec)
  178. return traits_type::eof();
  179. return c;
  180. }
  181. }
  182. else
  183. {
  184. // Send all data in the output buffer.
  185. asio::const_buffer buffer =
  186. asio::buffer(pbase(), pptr() - pbase());
  187. while (asio::buffer_size(buffer) > 0)
  188. {
  189. asio::error_code ec;
  190. std::size_t bytes_transferred = this->service.send(
  191. this->implementation, asio::buffer(buffer),
  192. 0, ec);
  193. if (ec)
  194. return traits_type::eof();
  195. buffer = buffer + bytes_transferred;
  196. }
  197. setp(put_buffer_.begin(), put_buffer_.end());
  198. // If the new character is eof then our work here is done.
  199. if (traits_type::eq_int_type(c, traits_type::eof()))
  200. return traits_type::not_eof(c);
  201. // Add the new character to the output buffer.
  202. *pptr() = traits_type::to_char_type(c);
  203. pbump(1);
  204. return c;
  205. }
  206. }
  207. int sync()
  208. {
  209. return overflow(traits_type::eof());
  210. }
  211. std::streambuf* setbuf(char_type* s, std::streamsize n)
  212. {
  213. if (pptr() == pbase() && s == 0 && n == 0)
  214. {
  215. unbuffered_ = true;
  216. setp(0, 0);
  217. return this;
  218. }
  219. return 0;
  220. }
  221. private:
  222. void init_buffers()
  223. {
  224. setg(get_buffer_.begin(),
  225. get_buffer_.begin() + putback_max,
  226. get_buffer_.begin() + putback_max);
  227. if (unbuffered_)
  228. setp(0, 0);
  229. else
  230. setp(put_buffer_.begin(), put_buffer_.end());
  231. }
  232. template <typename ResolverQuery>
  233. void resolve_and_connect(const ResolverQuery& query,
  234. asio::error_code& ec)
  235. {
  236. typedef typename Protocol::resolver resolver_type;
  237. typedef typename resolver_type::iterator iterator_type;
  238. resolver_type resolver(
  239. boost::base_from_member<asio::io_service>::member);
  240. iterator_type i = resolver.resolve(query, ec);
  241. if (!ec)
  242. {
  243. iterator_type end;
  244. ec = asio::error::host_not_found;
  245. while (ec && i != end)
  246. {
  247. this->basic_socket<Protocol, StreamSocketService>::close();
  248. this->basic_socket<Protocol, StreamSocketService>::connect(*i, ec);
  249. ++i;
  250. }
  251. }
  252. }
  253. enum { putback_max = 8 };
  254. enum { buffer_size = 512 };
  255. boost::array<char, buffer_size> get_buffer_;
  256. boost::array<char, buffer_size> put_buffer_;
  257. bool unbuffered_;
  258. };
  259. } // namespace asio
  260. #undef ASIO_PRIVATE_CONNECT_DEF
  261. #endif // !defined(BOOST_NO_IOSTREAM)
  262. #include "asio/detail/pop_options.hpp"
  263. #endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP