From 1fa3ca5f24c018126333ca2d6609730e1ae17386 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Tue, 10 Apr 2007 21:48:23 +0000 Subject: Added more comments, help, and socket actually reads and writes some, but it's not done. I need to decide how I want to do the buffering... --- src/archive.h | 47 +++++++++++ src/exceptions.cpp | 1 + src/exceptions.h | 2 + src/socket.cpp | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/socket.h | 23 +++++- src/stream.h | 10 +++ 6 files changed, 312 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/archive.h b/src/archive.h index 9ac3303..2f782d3 100644 --- a/src/archive.h +++ b/src/archive.h @@ -9,6 +9,53 @@ namespace Bu { + /** + * Provides a framework for serialization of objects and primitives. The + * archive will handle any basic primitive, a few special types, like char * + * strings, as well as STL classes and anything that inherits from the + * Archival class. Each Archive operates on a Stream, so you can send the + * data using an Archive almost anywhere. + * + * In order to use an Archive to store something to a file, try something + * like: + *@code + * File sOut("output", "wb"); // This is a stream subclass + * Archive ar( sOut, Archive::save ); + * ar << myClass; + @endcode + * In this example myClass is any class that inherits from Archival. When + * the storage operator is called, the Archival::archive() function in the + * myClass object is called with a reference to the Archive. This can be + * handled in one of two ways: + *@code + * void MyClass::archive( Archive &ar ) + * { + * ar && sName && nAge && sJob; + * } + @endcode + * Here we don't worry about weather we're loading or saving by using the + * smart && operator. This allows us to write very consistent, very simple + * archive functions that really do a lot of work. If we wanted to do + * something different in the case of loading or saving we would do: + *@code + * void MyClass::archive( Archive &ar ) + * { + * if( ar.isLoading() ) + * { + * ar >> sName >> nAge >> sJob; + * } else + * { + * ar << sName << nAge << sJob; + * } + * } + @endcode + * Archive currently does not provide facility to make fully portable + * archives. For example, it will not convert between endianness for you, + * nor will it take into account differences between primitive sizes on + * different platforms. This, at the moment, is up to the user to ensure. + * One way of dealing with the latter problem is to make sure and use + * explicit primitive types from the stdint.h header, i.e. int32_t. + */ class Archive { private: diff --git a/src/exceptions.cpp b/src/exceptions.cpp index 37f09a4..a512105 100644 --- a/src/exceptions.cpp +++ b/src/exceptions.cpp @@ -7,4 +7,5 @@ namespace Bu subExceptionDef( FileException ) subExceptionDef( ConnectionException ) subExceptionDef( PluginException ) + subExceptionDef( UnsupportedException ) } diff --git a/src/exceptions.h b/src/exceptions.h index b28d292..3efa19f 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -10,6 +10,7 @@ namespace Bu subExceptionDecl( FileException ) subExceptionDecl( ConnectionException ) subExceptionDecl( PluginException ) + subExceptionDecl( UnsupportedException ) enum eFileException { @@ -19,6 +20,7 @@ namespace Bu enum eConnectionException { excodeReadError, + excodeWriteError, excodeBadReadError, excodeConnectionClosed, excodeSocketTimeout diff --git a/src/socket.cpp b/src/socket.cpp index c5c592b..e206bb5 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -1,10 +1,240 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "socket.h" +#include "exceptions.h" -Bu::Socket::Socket() +#define RBS (1024*2) + +Bu::Socket::Socket( int nSocket ) : + nSocket( nSocket ), + bActive( true ) { } +Bu::Socket::Socket( const Bu::FString &sAddr, int nPort, int nTimeout ) +{ + struct sockaddr_in xServerName; + bActive = false; + + /* Create the socket. */ + nSocket = socket( PF_INET, SOCK_STREAM, 0 ); + + if( nSocket < 0 ) + { + throw ExceptionBase("Couldn't create socket.\n"); + } + + // These lines set the socket to non-blocking, a good thing? + int flags; + flags = fcntl(nSocket, F_GETFL, 0); + flags |= O_NONBLOCK; + if (fcntl(nSocket, F_SETFL, flags) < 0) + { + throw ExceptionBase("Couldn't set socket options.\n"); + } + + /* Connect to the server. */ + //printf("Resolving hostname (%s)...\n", sAddr ); + { + struct hostent *hostinfo; + + xServerName.sin_family = AF_INET; + xServerName.sin_port = htons( nPort ); + hostinfo = gethostbyname( sAddr.getStr() ); + if (hostinfo == NULL) + { + throw ExceptionBase("Couldn't resolve hostname.\n"); + } + xServerName.sin_addr = *(struct in_addr *) hostinfo->h_addr; + } + + //printf("Making actual connection..."); + //fflush( stdout ); + connect( + nSocket, + (struct sockaddr *)&xServerName, + sizeof(xServerName) + ); + //printf("Connected.\n"); + + bActive = true; + + if( nTimeout > 0 ) + { + fd_set rfds, wfds, efds; + int retval; + + FD_ZERO(&rfds); + FD_SET(nSocket, &rfds); + FD_ZERO(&wfds); + FD_SET(nSocket, &wfds); + FD_ZERO(&efds); + FD_SET(nSocket, &efds); + + struct timeval tv; + tv.tv_sec = nTimeout; + tv.tv_usec = 0; + + retval = select( nSocket+1, &rfds, &wfds, &efds, &tv ); + + if( retval == 0 ) + { + close(); + throw ExceptionBase("Connection timeout.\n"); + } + + } +} + Bu::Socket::~Socket() { } +void Bu::Socket::close() +{ + if( bActive ) + { + fsync( nSocket ); + ::close( nSocket ); + } + bActive = false; + //xInputBuf.clearData(); + //xOutputBuf.clearData(); + //if( pProtocol != NULL ) + //{ + // delete pProtocol; + // pProtocol = NULL; + //} +} + +void Bu::Socket::read() +{ + char buffer[RBS]; + int nbytes; + int nTotalRead=0; + + for(;;) + { + //memset( buffer, 0, RBS ); + + nbytes = ::read( nSocket, buffer, RBS ); + if( nbytes < 0 && errno != 0 && errno != EAGAIN ) + { + //printf("errno: %d, %s\n", errno, strerror( errno ) ); + /* Read error. */ + //perror("readInput"); + throw ConnectionException( + excodeReadError, + "Read error: %s", + strerror( errno ) + ); + } + else + { + if( nbytes <= 0 ) + break; + nTotalRead += nbytes; + sReadBuf.append( buffer, nbytes ); + /* Data read. */ + if( nbytes < RBS ) + { + break; + } + + /* New test, if data is divisible by RBS bytes on some libs the + * read could block, this keeps it from happening. + */ + { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(nSocket, &rfds); + struct timeval tv = { 0, 0 }; + int retval = select( nSocket+1, &rfds, NULL, NULL, &tv ); + if( retval == -1 ) + throw ConnectionException( + excodeBadReadError, + "Bad Read error" + ); + if( !FD_ISSET( nSocket, &rfds ) ) + break; + } + } + } + + /* + if( pProtocol != NULL && nTotalRead > 0 ) + { + pProtocol->onNewData(); + }*/ +} + +size_t Bu::Socket::read( void *pBuf, size_t nBytes ) +{ + read(); + + + + return sReadBuf.getSize(); +} + +size_t Bu::Socket::write( const void *pBuf, size_t nBytes ) +{ + int nWrote = TEMP_FAILURE_RETRY( ::write( nSocket, pBuf, nBytes ) ); + if( nWrote < 0 ) + { + throw ConnectionException( excodeWriteError, strerror(errno) ); + } + return nWrote; +} + +long Bu::Socket::tell() +{ + throw UnsupportedException(); +} + +void Bu::Socket::seek( long offset ) +{ + throw UnsupportedException(); +} + +void Bu::Socket::setPos( long pos ) +{ + throw UnsupportedException(); +} + +void Bu::Socket::setPosEnd( long pos ) +{ + throw UnsupportedException(); +} + +bool Bu::Socket::isEOS() +{ + return !bActive; +} + +bool Bu::Socket::canRead() +{ + return true; +} + +bool Bu::Socket::canWrite() +{ + return true; +} + +bool Bu::Socket::canSeek() +{ + return false; +} + diff --git a/src/socket.h b/src/socket.h index 8ccde71..3d0125d 100644 --- a/src/socket.h +++ b/src/socket.h @@ -4,6 +4,7 @@ #include #include "stream.h" +#include "fstring.h" namespace Bu { @@ -13,11 +14,29 @@ namespace Bu class Socket : public Stream { public: - Socket(); + Socket( int nSocket ); + Socket( const FString &sAddr, int nPort, int nTimeout=30 ); virtual ~Socket(); + + virtual void close(); + virtual void read(); + virtual size_t read( void *pBuf, size_t nBytes ); + virtual size_t write( const void *pBuf, size_t nBytes ); - private: + virtual long tell(); + virtual void seek( long offset ); + virtual void setPos( long pos ); + virtual void setPosEnd( long pos ); + virtual bool isEOS(); + + virtual bool canRead(); + virtual bool canWrite(); + virtual bool canSeek(); + private: + int nSocket; + bool bActive; + FString sReadBuf; }; } diff --git a/src/stream.h b/src/stream.h index ae94234..e640959 100644 --- a/src/stream.h +++ b/src/stream.h @@ -6,6 +6,16 @@ namespace Bu { + /** + * The basis for a completely general data transport mechanism. Anything + * that inherits from this should provide at least the basic read and/or + * write functions, and very probably the close function. Any functions + * that aren't supported should throw an exception if called. + * + * The constructor of a child class should pretty much universally be used + * to open the stream. I can't think of anything that should require an + * exception. + */ class Stream { public: -- cgit v1.2.3