#ifndef ARABICA_SOCKET_STREAM_H #define ARABICA_SOCKET_STREAM_H /////////////////////////////////////////////////////////////////////// // // socket_stream.h // // Written by Jez Higgins // Copyright 1999-2003 Jez UK Ltd, http://www.jezuk.co.uk/ // /////////////////////////////////////////////////////////////////////// // $Id$ /////////////////////////////////////////////////////////////////////// #include #ifndef USE_WINSOCK #include #include #include #include #include #include #include #else #include #endif #include #include #include #include #include #include #ifndef INADDR_NONE # define INADDR_NONE ((in_addr_t) -1) #endif #ifdef _MSC_VER #pragma warning(disable: 4250) // See http://connect.microsoft.com/VisualStudio/feedback/details/733720/inheriting-from-std-fstream-produces-c4250-warning #endif namespace Arabica { namespace io { /////////////////////////////////////////////////////////// // basic_socketbuf declaration template class basic_socketbuf : public std::basic_streambuf { public: typedef typename traitsT::int_type int_type; #if defined(USE_WINSOCK) && defined(UINTPTR_MAX) typedef std::uintptr_t socket_type; #else typedef int socket_type; #endif using std::basic_streambuf::setp; using std::basic_streambuf::setg; using std::basic_streambuf::underflow; using std::basic_streambuf::gptr; using std::basic_streambuf::gbump; using std::basic_streambuf::egptr; using std::basic_streambuf::eback; using std::basic_streambuf::pptr; using std::basic_streambuf::sputc; basic_socketbuf(); virtual ~basic_socketbuf(); bool is_open() const; basic_socketbuf* open(const char* hostname, unsigned short port); basic_socketbuf* close(); protected: virtual int_type overflow(int_type c = traitsT::eof()); virtual int sync(); virtual int_type underflow(); virtual int_type pbackfail(int_type c); private: typedef typename traitsT::state_type state_t; socket_type sock_; std::vector outBuffer_; state_t outState_; std::vector inBuffer_; state_t inState_; void growOutBuffer(); bool writeSocket(); void growInBuffer(); int readSocket(); int closeSocket(socket_type sock) const; static const size_t bufferSize_; static const size_t pbSize_; #ifndef USE_WINSOCK static const int INVALID_SOCKET; static const int SOCKET_ERROR; #endif }; // class basic_socketbuf template const size_t basic_socketbuf::bufferSize_ = 1024; template const size_t basic_socketbuf::pbSize_ = 4; // why 4? both Josuttis and Langer&Kreft use 4. #ifndef USE_WINSOCK template const int basic_socketbuf::INVALID_SOCKET = -1; template const int basic_socketbuf::SOCKET_ERROR = -1; #endif /////////////////////////////////////////////////////////// // basic_socketbuf definition template basic_socketbuf::basic_socketbuf() : std::basic_streambuf(), sock_(INVALID_SOCKET), outBuffer_(0), inBuffer_(0) { // outState_ = 0; // inState_ = 0; setp(0, 0); setg(0, 0, 0); } // basic_socketbuf template basic_socketbuf::~basic_socketbuf() { if(is_open()) { sync(); closeSocket(sock_); } // if(is_open()) } // ~basic_socketbuf template bool basic_socketbuf::is_open() const { return (sock_ != INVALID_SOCKET); } // is_open template basic_socketbuf* basic_socketbuf::open(const char* hostname, unsigned short port) { // already open? and crappy data if((sock_ != INVALID_SOCKET) || (hostname == 0) || (strlen(hostname) == 0)) return 0; // set up address sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = AF_INET; sockAddr.sin_addr.s_addr = inet_addr(hostname); if(sockAddr.sin_addr.s_addr == INADDR_NONE) { hostent* host = gethostbyname(hostname); if(!host) return 0; sockAddr.sin_addr.s_addr = reinterpret_cast(host->h_addr)->s_addr; } // if(sockAddr.sin_addr.s_addr == INADDR_NONE) sockAddr.sin_port = htons(port); // connect int tmpsock = static_cast(socket(AF_INET, SOCK_STREAM, 0)); if(tmpsock == INVALID_SOCKET) return 0; if(connect(tmpsock, reinterpret_cast(&sockAddr), sizeof(sockaddr_in)) != 0) { closeSocket(tmpsock); return 0; } // hurray, we've connected so initialise everything else we need to sock_ = tmpsock; return this; } // open template basic_socketbuf* basic_socketbuf::close() { if(!is_open()) return 0; if(closeSocket(sock_) == SOCKET_ERROR) return 0; sock_ = INVALID_SOCKET; setg(0,0,0); return this; } // close; template typename basic_socketbuf::int_type basic_socketbuf::overflow(typename basic_socketbuf::int_type c) { if(traitsT::eq_int_type(traitsT::eof(), c)) return traitsT::not_eof(c); if(!is_open()) return traitsT::eof(); growOutBuffer(); sputc(traitsT::to_char_type(c)); return traitsT::not_eof(c); } // overflow template int basic_socketbuf::sync() { return writeSocket() ? 0 : -1; } // sync template typename basic_socketbuf::int_type basic_socketbuf::underflow() { if(!is_open()) return traitsT::eof(); if(gptr() != 0 && gptr() < egptr()) return (traitsT::to_int_type(*gptr())); size_t length = readSocket(); if(!length) return traitsT::eof(); return traitsT::to_int_type(*gptr()); } // underflow template typename basic_socketbuf::int_type basic_socketbuf::pbackfail(int_type c) { if(gptr() == eback()) return traitsT::eof(); gbump(-1); if(!traitsT::eq_int_type(c, traitsT::eof())) *(gptr()) = traitsT::to_char_type(c); return traitsT::not_eof(c); } // pbackfail template void basic_socketbuf::growOutBuffer() { size_t oldsize = outBuffer_.capacity(); size_t newsize = (oldsize ? oldsize*2 : bufferSize_); outBuffer_.resize(newsize); char* out_begin = &(outBuffer_[0]); setp(out_begin + oldsize, out_begin + newsize); } // growOutBuffer template bool basic_socketbuf::writeSocket() { // write to the socket charT* from_next = &(outBuffer_[0]); size_t length = pptr() - from_next; if(!length) return true; bool ok = (send(sock_, from_next, static_cast(length), 0) != SOCKET_ERROR); if(ok) setp(from_next, from_next + outBuffer_.capacity()); return ok; } // writeSocket template void basic_socketbuf::growInBuffer() { size_t oldsize = inBuffer_.capacity(); size_t newsize = (oldsize ? oldsize*2 : bufferSize_+pbSize_); inBuffer_.resize(newsize); } // growInBuffer template int basic_socketbuf::readSocket() { if(!inBuffer_.capacity()) growInBuffer(); size_t pbCount = std::min(gptr() - eback(), pbSize_); memcpy(&(inBuffer_[0]) + (pbSize_-pbCount)*sizeof(charT), gptr() - pbCount*sizeof(charT), pbCount*sizeof(charT)); int res = recv(sock_, &(inBuffer_[0]) + pbSize_, static_cast(inBuffer_.capacity() - pbSize_), 0); if(res == 0) { // server closed the socket close(); return 0; } // if(res == 0) else if(res == SOCKET_ERROR) { #ifdef USE_WINSOCK if(GetLastError() == WSAEMSGSIZE) { // buffer was too small, so make it bigger growInBuffer(); return readSocket(); } // if(GetLastError() != WSAEMSGSIZE) #endif // unclever error handling close(); return 0; } // if(res == SOCKET_ERROR) charT* to_begin = &(inBuffer_[0]) + pbSize_; setg(to_begin - pbCount, to_begin, to_begin + res); return res; } // readSocket template int basic_socketbuf::closeSocket(socket_type sock) const { #ifdef USE_WINSOCK return closesocket(sock); #else return ::close(sock); #endif } // closeSocket /////////////////////////////////////////////////////////// // basic_socketstream declaration template class socketstreambuf_init { public: typedef basic_socketbuf sockbuf; sockbuf* buf() const { return &buf_; } // buf() private: mutable sockbuf buf_; }; // class socketstreambuf_init template class basic_socketstream : private virtual socketstreambuf_init, public std::basic_iostream { public: using std::basic_iostream::setstate; using std::basic_iostream::badbit; basic_socketstream(); explicit basic_socketstream(const char* hostname, int port); virtual ~basic_socketstream(); basic_socketbuf* rdbuf() const; bool is_open() const; void open(const char* hostname, unsigned short port); void close(); }; // class basic_socketstream //////////////////////////////////////////////////////////////// // basic_socketstream definition template basic_socketstream::basic_socketstream() : socketstreambuf_init(), std::basic_iostream(socketstreambuf_init::buf()) { } // basic_socketstream template basic_socketstream::basic_socketstream(const char* hostname, int port) : socketstreambuf_init(), std::basic_iostream(socketstreambuf_init::buf()) { open(hostname, port); } // basic_socketstream template basic_socketstream::~basic_socketstream() { } // ~basic_socketstream template basic_socketbuf* basic_socketstream::rdbuf() const { return socketstreambuf_init::buf(); } // rdbuf template bool basic_socketstream::is_open() const { return socketstreambuf_init::buf()->is_open(); } // is_open template void basic_socketstream::open(const char* hostname, unsigned short port) { if(socketstreambuf_init::buf()->open(hostname, port) == 0) setstate(badbit); } // open template void basic_socketstream::close() { if(!is_open()) return; if(socketstreambuf_init::buf()->close() == 0) setstate(badbit); } // close typedef basic_socketbuf > socketbuf; typedef basic_socketstream > socketstream; #ifndef ARABICA_NO_WSTRING_T typedef basic_socketbuf > wsocketbuf; typedef basic_socketstream > wsocketstream; #endif } // namespace io } // namespace Arabica #endif //end of file