Bytemaster's Boost Libraries
|
00001 // 00002 // basic_socket_streambuf.hpp 00003 // ~~~~~~~~~~~~~~~~~~~~~~~~~~ 00004 // 00005 // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) 00006 // 00007 // Distributed under the Boost Software License, Version 1.0. (See accompanying 00008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 00009 // 00010 00011 #ifndef BOOST_CMT_ASIO_BASIC_SOCKET_STREAMBUF_HPP 00012 #define BOOST_CMT_ASIO_BASIC_SOCKET_STREAMBUF_HPP 00013 00014 #if defined(_MSC_VER) && (_MSC_VER >= 1200) 00015 # pragma once 00016 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 00017 00018 #include <boost/asio/detail/config.hpp> 00019 00020 #if !defined(BOOST_NO_IOSTREAM) 00021 00022 #include <streambuf> 00023 #include <boost/array.hpp> 00024 #include <boost/preprocessor/arithmetic/inc.hpp> 00025 #include <boost/preprocessor/repetition/enum_binary_params.hpp> 00026 #include <boost/preprocessor/repetition/enum_params.hpp> 00027 #include <boost/preprocessor/repetition/repeat_from_to.hpp> 00028 #include <boost/asio/basic_socket.hpp> 00029 #include <boost/asio/detail/throw_error.hpp> 00030 #include <boost/asio/io_service.hpp> 00031 #include <boost/asio/stream_socket_service.hpp> 00032 #include <boost/cmt/asio.hpp> 00033 00034 #if !defined(BOOST_ASIO_SOCKET_STREAMBUF_MAX_ARITY) 00035 #define BOOST_ASIO_SOCKET_STREAMBUF_MAX_ARITY 5 00036 #endif // !defined(BOOST_ASIO_SOCKET_STREAMBUF_MAX_ARITY) 00037 00038 // A macro that should expand to: 00039 // template <typename T1, ..., typename Tn> 00040 // basic_socket_streambuf<Protocol, StreamSocketService>* connect( 00041 // T1 x1, ..., Tn xn) 00042 // { 00043 // init_buffers(); 00044 // boost::system::error_code ec; 00045 // this->basic_socket<Protocol, StreamSocketService>::close(ec); 00046 // typedef typename Protocol::resolver resolver_type; 00047 // typedef typename resolver_type::query resolver_query; 00048 // resolver_query query(x1, ..., xn); 00049 // resolve_and_connect(query, ec); 00050 // return !ec ? this : 0; 00051 // } 00052 // This macro should only persist within this file. 00053 00054 #define BOOST_ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \ 00055 template <BOOST_PP_ENUM_PARAMS(n, typename T)> \ 00056 basic_socket_streambuf<Protocol, StreamSocketService>* connect( \ 00057 BOOST_PP_ENUM_BINARY_PARAMS(n, T, x)) \ 00058 { \ 00059 init_buffers(); \ 00060 boost::system::error_code ec; \ 00061 this->basic_socket<Protocol, StreamSocketService>::close(ec); \ 00062 typedef typename Protocol::resolver resolver_type; \ 00063 typedef typename resolver_type::query resolver_query; \ 00064 resolver_query query(BOOST_PP_ENUM_PARAMS(n, x)); \ 00065 resolve_and_connect(query, ec); \ 00066 return !ec ? this : 0; \ 00067 } \ 00068 00069 00070 #include <boost/asio/detail/push_options.hpp> 00071 00072 00073 namespace boost { 00074 namespace cmt { 00075 namespace asio { 00076 00077 using namespace boost::asio; 00078 00080 template <typename Protocol, 00081 typename StreamSocketService = stream_socket_service<Protocol> > 00082 class basic_socket_streambuf 00083 : public std::streambuf, 00084 public basic_socket<Protocol, StreamSocketService> 00085 { 00086 public: 00088 typedef typename Protocol::endpoint endpoint_type; 00089 00091 basic_socket_streambuf() 00092 : basic_socket<Protocol, StreamSocketService>( 00093 boost::cmt::asio::default_io_service() ), 00094 unbuffered_(false) 00095 { 00096 init_buffers(); 00097 } 00098 00100 virtual ~basic_socket_streambuf() 00101 { 00102 if (pptr() != pbase()) 00103 overflow(traits_type::eof()); 00104 } 00105 00107 00113 basic_socket_streambuf<Protocol, StreamSocketService>* connect( 00114 const endpoint_type& endpoint) 00115 { 00116 init_buffers(); 00117 boost::system::error_code ec; 00118 this->basic_socket<Protocol, StreamSocketService>::close(ec); 00119 //TODO: MAKE ASYNC 00120 stack_retainable<promise<boost::system::error_code> > p; 00121 this->basic_socket<Protocol, StreamSocketService>::async_connect(endpoint, 00122 boost::bind( boost::cmt::asio::detail::error_handler_ec, &p, _1 )); 00123 return !p.wait() ? this : 0; 00124 } 00125 00126 #if defined(GENERATING_DOCUMENTATION) 00127 00128 00136 template <typename T1, ..., typename TN> 00137 basic_socket_streambuf<Protocol, StreamSocketService>* connect( 00138 T1 t1, ..., TN tn); 00139 #else 00140 BOOST_PP_REPEAT_FROM_TO( 00141 1, BOOST_PP_INC(BOOST_ASIO_SOCKET_STREAMBUF_MAX_ARITY), 00142 BOOST_ASIO_PRIVATE_CONNECT_DEF, _ ) 00143 #endif 00144 00146 00150 basic_socket_streambuf<Protocol, StreamSocketService>* close() 00151 { 00152 boost::system::error_code ec; 00153 sync(); 00154 this->basic_socket<Protocol, StreamSocketService>::close(ec); 00155 if (!ec) 00156 init_buffers(); 00157 return !ec ? this : 0; 00158 } 00159 00160 protected: 00161 int_type underflow() 00162 { 00163 if (gptr() == egptr()) 00164 { 00165 boost::system::error_code ec; 00166 stack_retainable<promise<size_t> > p; 00167 this->service.async_receive( 00168 this->implementation, 00169 boost::asio::buffer(boost::asio::buffer(get_buffer_) + putback_max), 00170 0, boost::bind( boost::cmt::asio::detail::read_write_handler_ec, &p, &ec, _1, _2 )); 00171 std::size_t bytes_transferred = p.wait(); 00172 if (ec) 00173 return traits_type::eof(); 00174 setg(get_buffer_.begin(), get_buffer_.begin() + putback_max, 00175 get_buffer_.begin() + putback_max + bytes_transferred); 00176 return traits_type::to_int_type(*gptr()); 00177 } 00178 else 00179 { 00180 return traits_type::eof(); 00181 } 00182 } 00183 00184 int_type overflow(int_type c) 00185 { 00186 if (unbuffered_) 00187 { 00188 if (traits_type::eq_int_type(c, traits_type::eof())) 00189 { 00190 // Nothing to do. 00191 return traits_type::not_eof(c); 00192 } 00193 else 00194 { 00195 // Send the single character immediately. 00196 boost::system::error_code ec; 00197 char_type ch = traits_type::to_char_type(c); 00198 00199 // boost::cmt::asio::write( *this, 00200 // boost::asio::buffer(&ch, sizeof(char_type)), ec); 00201 00202 // this->service.send(this->implementation, 00203 // boost::asio::buffer(&ch, sizeof(char_type)), 0, ec); 00204 stack_retainable<promise<size_t> > p; 00205 this->service.async_send( 00206 this->implementation, 00207 boost::asio::buffer(&ch, sizeof(char_type)), 00208 0, boost::bind( boost::cmt::asio::detail::read_write_handler_ec, &p, &ec, _1, _2 )); 00209 std::size_t bytes_transferred = p.wait(); 00210 if (ec) 00211 return traits_type::eof(); 00212 return c; 00213 } 00214 } 00215 else 00216 { 00217 // Send all data in the output buffer. 00218 boost::asio::const_buffer buffer = 00219 boost::asio::buffer(pbase(), pptr() - pbase()); 00220 while (boost::asio::buffer_size(buffer) > 0) 00221 { 00222 boost::system::error_code ec; 00223 // std::size_t bytes_transferred = boost::cmt::asio::write_some( this->implementation, 00224 // boost::asio::buffer(buffer), ec); 00225 00226 stack_retainable<promise<size_t> > p; 00227 this->service.async_send( 00228 this->implementation, boost::asio::buffer(buffer), 00229 0, boost::bind( boost::cmt::asio::detail::read_write_handler_ec, &p, &ec, _1, _2 )); 00230 std::size_t bytes_transferred = p.wait(); 00231 if (ec) 00232 return traits_type::eof(); 00233 buffer = buffer + bytes_transferred; 00234 } 00235 setp(put_buffer_.begin(), put_buffer_.end()); 00236 00237 // If the new character is eof then our work here is done. 00238 if (traits_type::eq_int_type(c, traits_type::eof())) 00239 return traits_type::not_eof(c); 00240 00241 // Add the new character to the output buffer. 00242 *pptr() = traits_type::to_char_type(c); 00243 pbump(1); 00244 return c; 00245 } 00246 } 00247 00248 int sync() 00249 { 00250 return overflow(traits_type::eof()); 00251 } 00252 00253 std::streambuf* setbuf(char_type* s, std::streamsize n) 00254 { 00255 if (pptr() == pbase() && s == 0 && n == 0) 00256 { 00257 unbuffered_ = true; 00258 setp(0, 0); 00259 return this; 00260 } 00261 00262 return 0; 00263 } 00264 00265 private: 00266 00267 void init_buffers() 00268 { 00269 setg(get_buffer_.begin(), 00270 get_buffer_.begin() + putback_max, 00271 get_buffer_.begin() + putback_max); 00272 if (unbuffered_) 00273 setp(0, 0); 00274 else 00275 setp(put_buffer_.begin(), put_buffer_.end()); 00276 } 00277 00278 template <typename ResolverQuery> 00279 void resolve_and_connect(const ResolverQuery& query, 00280 boost::system::error_code& ec) 00281 { 00282 typedef typename Protocol::resolver resolver_type; 00283 typedef typename resolver_type::iterator iterator_type; 00284 resolver_type resolver( 00285 boost::cmt::asio::default_io_service() ); 00286 iterator_type i = resolver.resolve(query, ec); 00287 if (!ec) 00288 { 00289 iterator_type end; 00290 ec = boost::asio::error::host_not_found; 00291 while (ec && i != end) 00292 { 00293 this->basic_socket<Protocol, StreamSocketService>::close(); 00294 this->basic_socket<Protocol, StreamSocketService>::connect(*i, ec); 00295 ++i; 00296 } 00297 } 00298 } 00299 00300 enum { putback_max = 8 }; 00301 enum { buffer_size = 512 }; 00302 boost::array<char, buffer_size> get_buffer_; 00303 boost::array<char, buffer_size> put_buffer_; 00304 bool unbuffered_; 00305 }; 00306 } // namespace asio 00307 } // namespace cmt 00308 } // namespace boost 00309 00310 #include <boost/asio/detail/pop_options.hpp> 00311 00312 #undef BOOST_ASIO_PRIVATE_CONNECT_DEF 00313 00314 #endif // !defined(BOOST_NO_IOSTREAM) 00315 00316 #endif // BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP