From 268fd2c37de6c618c8d200d308ff03750f7be785 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 30 Jul 2010 02:45:10 +0200 Subject: [PATCH] Refactoring naar common bases - also, werkende RTMP streaming! Jammer dat er nog maar 1 frame wordt verwerkt... maar het werkt! --- sockets/SocketW.h | 22 ++ sockets/sw_base.cpp | 764 ++++++++++++++++++++++++++++++++++++++++++ sockets/sw_base.h | 205 ++++++++++++ sockets/sw_inet.cpp | 249 ++++++++++++++ sockets/sw_inet.h | 48 +++ sockets/sw_internal.h | 75 +++++ sockets/sw_unix.cpp | 99 ++++++ sockets/sw_unix.h | 41 +++ util/flv.cpp | 69 ++++ 9 files changed, 1572 insertions(+) create mode 100644 sockets/SocketW.h create mode 100644 sockets/sw_base.cpp create mode 100644 sockets/sw_base.h create mode 100644 sockets/sw_inet.cpp create mode 100644 sockets/sw_inet.h create mode 100644 sockets/sw_internal.h create mode 100644 sockets/sw_unix.cpp create mode 100644 sockets/sw_unix.h create mode 100644 util/flv.cpp diff --git a/sockets/SocketW.h b/sockets/SocketW.h new file mode 100644 index 00000000..b7096c0c --- /dev/null +++ b/sockets/SocketW.h @@ -0,0 +1,22 @@ +// C++ Socket Wrapper +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindstr�m + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#ifndef SocketW_H +#define SocketW_H + +#include "sw_base.h" +#include "sw_unix.h" +#include "sw_inet.h" + +#endif //SocketW_H diff --git a/sockets/sw_base.cpp b/sockets/sw_base.cpp new file mode 100644 index 00000000..23ca1eb6 --- /dev/null +++ b/sockets/sw_base.cpp @@ -0,0 +1,764 @@ +// C++ Socket Wrapper +// SocketW base class +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindstr�m + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#include "sw_base.h" +#include +#include +#include +#include +#include + +#ifndef __WIN32__ + #include + #include + #include + #include + #include + + #define INVALID_SOCKET -1 //avoid M$ braindamage +#else + //why use POSIX standards when we can make our own + //and frustrate people? (well known M$ policy) + + #ifndef EBADF + #define EBADF WSAEBADF + #endif + + #define ENOTSOCK WSAENOTSOCK + #define EOPNOTSUPP WSAEOPNOTSUPP + #define EADDRINUSE WSAEADDRINUSE + #define EWOULDBLOCK WSAEWOULDBLOCK + #define EMSGSIZE WSAEMSGSIZE + #define EINPROGRESS WSAEINPROGRESS + #define EALREADY WSAEALREADY + #define ECONNREFUSED WSAECONNREFUSED + #define ETIMEDOUT WSAETIMEDOUT + #define ENOTCONN WSAENOTCONN + + #ifndef EINTR + #define EINTR WSAEINTR + #endif +#endif + +#ifndef MSG_NOSIGNAL + #define MSG_NOSIGNAL 0 +#endif + +// Socklen hack +#if defined(__linux__) || defined(__FreeBSD__) // || defined(__bsdi__) || defined(__NetBSD__) too, perhaps? Bugreports, please! + #define sw_socklen_t socklen_t +#elif defined(__WIN32__) || defined(__osf__) + #define sw_socklen_t int +#else + #define sw_socklen_t unsigned int +#endif + + +using namespace std; + +#ifdef __WIN32__ +//Win32 braindamage +int close(int fd) +{ + return closesocket(fd); +} + +int fcntl(int fd, int cmd, long arg) +{ + unsigned long mode = arg; + + return WSAIoctl(fd, cmd, &mode, sizeof(unsigned long), NULL, 0, NULL, NULL, NULL); +} + +void WSA_exit(void) +{ + WSACleanup(); +} +#endif + + +//==================================================================== +//== Error handling mode +//==================================================================== +bool sw_DoThrow = false; +bool sw_Verbose = true; + +void sw_setThrowMode(bool throw_errors) +{ + sw_DoThrow = throw_errors; +} + +void sw_setVerboseMode(bool verbose) +{ + sw_Verbose = verbose; +} + +bool sw_getThrowMode(void) +{ + return sw_DoThrow; +} + +bool sw_getVerboseMode(void) +{ + return sw_Verbose; +} + + +//==================================================================== +//== Base error class +//==================================================================== +SWBaseSocket::SWBaseError::SWBaseError() +{ + be = ok; + error_string = ""; + failed_class = NULL; +} + +SWBaseSocket::SWBaseError::SWBaseError(base_error e) +{ + be = e; + error_string = ""; + failed_class = NULL; +} + +string SWBaseSocket::SWBaseError::get_error() +{ + return error_string; +} + +SWBaseSocket* SWBaseSocket::SWBaseError::get_failedClass(void) +{ + return failed_class; +} + +void SWBaseSocket::SWBaseError::set_errorString(string msg) +{ + error_string = msg; +} + +void SWBaseSocket::SWBaseError::set_failedClass(SWBaseSocket *pnt) +{ + failed_class = pnt; +} + +bool SWBaseSocket::SWBaseError::operator==(SWBaseError e) +{ + return be == e.be; +} + +bool SWBaseSocket::SWBaseError::operator!=(SWBaseError e) +{ + return be != e.be; +} + + +//==================================================================== +//== SWBaseSocket +//== Base class for sockets +//==================================================================== +SWBaseSocket::SWBaseSocket() +{ + //indicate nonopen + myfd = -1; + recv_close = false; + + //init values + error_string = ""; + block_mode = blocking; + fsend_ready = true; + frecv_ready = true; + tsec = 0; + tusec = 0; + + #ifdef __WIN32__ + //kick winsock awake + static bool firstuse = true; + if( firstuse == true ){ + WSAData wsaData; + int nCode; + if( (nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0 ){ + handle_errno(NULL, "SWBaseSocket - WSAStartup() failed: "); + exit(-1); // Should never happend + } + + //cleanup at exit + atexit(WSA_exit); + firstuse = false; + } + #endif /* __WIN32__ */ +} + +SWBaseSocket::~SWBaseSocket() +{ + if(myfd > 0) + close(myfd); +} + +bool SWBaseSocket::listen(int qLimit, SWBaseError *error) +{ + get_socket(); + + //Avoid "Address already in use" thingie + char yes=1; + setsockopt(myfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + + if(::listen(myfd, qLimit) == -1){ + handle_errno(error, "SWBaseSocket::listen() error: "); + return false; + } + + no_error(error); + return true; +} + +SWBaseSocket* SWBaseSocket::accept(SWBaseError *error) +{ + int remotefd = -1; + sockaddr remoteAdr; + + if( !waitRead(error) ) + return NULL; + + sw_socklen_t ssize = sizeof(sockaddr); + + if((remotefd = ::accept(myfd, &remoteAdr, &ssize)) == int(INVALID_SOCKET)){ + handle_errno(error, "SWBaseSocket::accept() error: "); + return NULL; + } + + //nonblocking? + if( block_mode == nonblocking ) + fcntl(remotefd, F_SETFL, O_NONBLOCK); + + /* Create new class*/ + SWBaseSocket* remoteClass = create(remotefd, error); + if( remoteClass == NULL ) + return NULL; + + no_error(error); + return remoteClass; +} + +bool SWBaseSocket::disconnect(SWBaseError *error) +{ + int n = 0; + char buf[256]; + + if(myfd < 0){ + set_error(error, notConnected, "SWBaseSocket::disconnect() - No connection"); + return false; + } + + //close WR (this signals the peer) + if( shutdown(myfd, 1) != 0 ){ + handle_errno(error, "SWBaseSocket::disconnect() error: "); + return false; + } + + + SWBaseError err; + + //wait for close signal from peer + if( recv_close == false ){ + while(true){ + if( !waitRead(error) ) + return false; + + n = recv(buf, 256, &err); + + if( n <= 0 ) + break; + if(block_mode == noWait){ + //we don't want to block + set_error(error, notReady, "SWBaseSocket::disconnect() - Need more time, call again"); + return false; + } + } + } + + if( n != 0 ){ + set_error(error, err, error_string); + return false; //error + } + + //reset state + reset(); + + close(myfd); + myfd = -1; + + no_error(error); + return true; +} + +bool SWBaseSocket::close_fd() +{ + if( myfd > 0 ){ + close(myfd); + myfd = -1; + + //reset state + reset(); + + return true; + } + return false; +} + +int SWBaseSocket::send(const char *buf, int bytes, SWBaseError *error) +{ + int ret; + + if(myfd < 0){ + set_error(error, notConnected, "SWBaseSocket::send() - No connection"); + return -1; + } + + if( !waitWrite(error) ) + return -1; + + ret = ::send(myfd, buf, bytes, MSG_NOSIGNAL); + + if( ret < 0 ) + handle_errno(error, "SWBaseSocket::send() error: "); + else + no_error(error); + + return ret; +} + +int SWBaseSocket::fsend(const char *buf, int bytes, SWBaseError *error) +{ + int n; + int bytessent; + + if(fsend_ready){ + //First call + fsend_bytesleft = bytes; + fsend_total = fsend_bytesleft; //global var needed for resume + bytessent = 0; + fsend_ready = false; //point of no return + } + else{ + //resume + bytessent = fsend_total - fsend_bytesleft; + } + + //send package + while( fsend_bytesleft > 0 ){ + n = send( buf + bytessent , fsend_bytesleft, error ); + + //return on error, wouldblock or nowait + if( n < 0 ) + return ( (bytessent > 0 )? -bytessent : -1 ); + + bytessent += n; + fsend_bytesleft -= n; + + if ( block_mode == noWait && fsend_bytesleft > 0 ){ + set_error(error, notReady, "SWBaseSocket::fsend() - Need more time, call again"); + return -bytessent; + } + } + + fsend_ready = true; + + no_error(error); + return fsend_total; +} + +int SWBaseSocket::sendmsg(const string msg, SWBaseError *error) +{ + return send(msg.c_str(), msg.size(), error); +} + +int SWBaseSocket::fsendmsg(const string msg, SWBaseError *error) +{ + return fsend(msg.c_str(), msg.size(), error); +} + +int SWBaseSocket::recv(char *buf, int bytes, SWBaseError *error) +{ + int ret; + + if(myfd < 0){ + set_error(error, notConnected, "SWBaseSocket::recv() - No connection"); + return -1; + } + + if( !waitRead(error) ) + return -1; + + ret = ::recv(myfd, buf, bytes, MSG_NOSIGNAL); + + if( ret < 0 ) + handle_errno(error, "SWBaseSocket::recv() error: "); + else if( ret == 0 ){ + recv_close = true; //we recived a close signal from peer + set_error(error, terminated, "SWBaseSocket::recv() - Connection terminated by peer"); + }else + no_error(error); + + return ret; +} + +int SWBaseSocket::frecv(char *buf, int bytes, SWBaseError *error) +{ + int n; + int bytesrecv; + + if(frecv_ready){ + //First call + frecv_bytesleft = bytes; + frecv_total = frecv_bytesleft; //global var needed for resume + bytesrecv = 0; + frecv_ready = false; //point of no return + } + else{ + //resume + bytesrecv = frecv_total - frecv_bytesleft; + } + + + //recv package + while( frecv_bytesleft > 0 ){ + n = recv( buf + bytesrecv , frecv_bytesleft, error ); + + //return on error, wouldblock, nowait or timeout + if( n < 0 ) + return ( (bytesrecv > 0 )? -bytesrecv : -1 ); + if( n == 0 ) + return 0; // terminated + + bytesrecv += n; + frecv_bytesleft -= n; + + if ( block_mode == noWait && frecv_bytesleft > 0 ){ + set_error(error, notReady, "SWBaseSocket::frecv() - Need more time, call again"); + return -bytesrecv; + } + } + + frecv_ready = true; + + no_error(error); + return frecv_total; +} + +string SWBaseSocket::recvmsg(int bytes, SWBaseError *error) +{ + char *buf = new char[bytes+1]; + + SWBaseError err; + string msg = ""; + int ret = recv(buf, bytes, &err); + + if( ret > 0 ){ + buf[ret]='\0'; // Make sure the string is null terminated + msg = buf; + no_error(error); + } + delete[] buf; + + if( ret < 1 ) + set_error(error, err, err.get_error()); + + return msg; +} + +int SWBaseSocket::get_fd(SWBaseError *error) +{ + if( myfd > 0 ){ + no_error(error); + return myfd; + } + + set_error(error, notConnected, "SWBaseSocket::get_fd() - No descriptor"); + return -1; +} + +bool SWBaseSocket::get_host(sockaddr *host, SWBaseError *error) +{ + if( host == NULL){ + set_error(error, fatal, "SWBaseSocket::get_host() - Got NULL pointer"); + return false; + } + + if(myfd < 0){ + set_error(error, notConnected, "SWBaseSocket::get_host() - No socket"); + return false; + } + + sw_socklen_t tmp = sizeof(sockaddr); + if( getsockname(myfd, host, &tmp) != 0 ){ + handle_errno(error, "SWBaseSocket::get_host() error: "); + return false; + } + + no_error(error); + return true; +} + +bool SWBaseSocket::get_peer(sockaddr *peer, SWBaseError *error) +{ + if( peer == NULL){ + set_error(error, fatal, "SWBaseSocket::get_peer() - Got NULL pointer"); + return false; + } + + if(myfd > 0){ + sw_socklen_t tmp = sizeof(sockaddr); + if( getpeername(myfd, peer, &tmp) != 0 ){ + handle_errno(error, "SWBaseSocket::get_peer() error: "); + return false; + } + }else{ + set_error(error, notConnected, "SWBaseSocket::get_peer() - No connection"); + return false; + } + + no_error(error); + return true; +} + +void SWBaseSocket::reset() +{ + // Reset flags + recv_close = false; + + fsend_ready = true; + frecv_ready = true; +} + +bool SWBaseSocket::waitIO(io_type &type, SWBaseError *error) +{ + if( block_mode != blocking ){ + no_error(error); + return true; + } + + // We prefere to wait with select() even if no timeout is set + // as select() behaves more predictable + + timeval t; + timeval *to = NULL; // Indicate "wait for ever" + t.tv_sec = tsec; + t.tv_usec = tusec; + + if( tsec > 0 || tusec > 0 ) + to = &t; + + fd_set readfds, writefds, exceptfds; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + FD_SET(myfd, &readfds); + FD_SET(myfd, &writefds); + FD_SET(myfd, &exceptfds); + + int ret = 0; + + switch (type){ + case read: + ret = select(myfd+1, &readfds, NULL, NULL, to); + break; + case write: + ret = select(myfd+1, NULL, &writefds, NULL, to); + break; + case except: + ret = select(myfd+1, NULL, NULL, &exceptfds, to); + break; + case rw: + ret = select(myfd+1, &readfds, &writefds, NULL, to); + break; + case all: + ret = select(myfd+1, &readfds, &writefds, &exceptfds, to); + break; + } + + if( ret < 0 ){ + handle_errno(error, "SWBaseSocket::waitIO() error: "); + return false; + } + if( ret == 0 ){ + set_error(error, timeout, "SWBaseSocket::waitIO() timeout"); + return false; + } + + if( FD_ISSET(myfd, &readfds) ){ + no_error(error); + type = read; + return true; + } + if( FD_ISSET(myfd, &writefds) ){ + no_error(error); + type = write; + return true; + } + if( FD_ISSET(myfd, &exceptfds) ){ + no_error(error); + type = except; + return true; + } + + set_error(error, fatal, "SWBaseSocket::waitIO() failed on select()"); + return false; +} + +bool SWBaseSocket::waitRead(SWBaseError *error) +{ + io_type tmp = read; + return waitIO(tmp, error); +} + +bool SWBaseSocket::waitWrite(SWBaseError *error) +{ + io_type tmp = write; + return waitIO(tmp, error); +} + +void SWBaseSocket::print_error() +{ + if( error_string.size() > 0 ) + fprintf(stderr, "%s!\n", error_string.c_str()); +} + +void SWBaseSocket::handle_errno(SWBaseError *error, string msg) +{ + #ifndef __WIN32__ + msg += strerror(errno); + #else + //stupid stupid stupid stupid M$ + switch (WSAGetLastError()){ + case 0: msg += "No error"; break; + case WSAEINTR: msg += "Interrupted system call"; break; + case WSAEBADF: msg += "Bad file number"; break; + case WSAEACCES: msg += "Permission denied"; break; + case WSAEFAULT: msg += "Bad address"; break; + case WSAEINVAL: msg += "Invalid argument"; break; + case WSAEMFILE: msg += "Too many open sockets"; break; + case WSAEWOULDBLOCK: msg += "Operation would block"; break; + case WSAEINPROGRESS: msg += "Operation now in progress"; break; + case WSAEALREADY: msg += "Operation already in progress"; break; + case WSAENOTSOCK: msg += "Socket operation on non-socket"; break; + case WSAEDESTADDRREQ: msg += "Destination address required"; break; + case WSAEMSGSIZE: msg += "Message too long"; break; + case WSAEPROTOTYPE: msg += "Protocol wrong type for socket"; break; + case WSAENOPROTOOPT: msg += "Bad protocol option"; break; + case WSAEPROTONOSUPPORT: msg += "Protocol not supported"; break; + case WSAESOCKTNOSUPPORT: msg += "Socket type not supported"; break; + case WSAEOPNOTSUPP: msg += "Operation not supported on socket"; break; + case WSAEPFNOSUPPORT: msg += "Protocol family not supported"; break; + case WSAEAFNOSUPPORT: msg += "Address family not supported"; break; + case WSAEADDRINUSE: msg += "Address already in use"; break; + case WSAEADDRNOTAVAIL: msg += "Can't assign requested address"; break; + case WSAENETDOWN: msg += "Network is down"; break; + case WSAENETUNREACH: msg += "Network is unreachable"; break; + case WSAENETRESET: msg += "Net connection reset"; break; + case WSAECONNABORTED: msg += "Software caused connection abort"; break; + case WSAECONNRESET: msg += "Connection reset by peer"; break; + case WSAENOBUFS: msg += "No buffer space available"; break; + case WSAEISCONN: msg += "Socket is already connected"; break; + case WSAENOTCONN: msg += "Socket is not connected"; break; + case WSAESHUTDOWN: msg += "Can't send after socket shutdown"; break; + case WSAETOOMANYREFS: msg += "Too many references"; break; + case WSAETIMEDOUT: msg += "Connection timed out"; break; + case WSAECONNREFUSED: msg += "Connection refused"; break; + case WSAELOOP: msg += "Too many levels of symbolic links"; break; + case WSAENAMETOOLONG: msg += "File name too long"; break; + case WSAEHOSTDOWN: msg += "Host is down"; break; + case WSAEHOSTUNREACH: msg += "No route to host"; break; + case WSAENOTEMPTY: msg += "Directory not empty"; break; + case WSAEPROCLIM: msg += "Too many processes"; break; + case WSAEUSERS: msg += "Too many users"; break; + case WSAEDQUOT: msg += "Disc quota exceeded"; break; + case WSAESTALE: msg += "Stale NFS file handle"; break; + case WSAEREMOTE: msg += "Too many levels of remote in path"; break; + case WSASYSNOTREADY: msg += "Network system is unavailable"; break; + case WSAVERNOTSUPPORTED: msg += "Winsock version out of range"; break; + case WSANOTINITIALISED: msg += "WSAStartup not yet called"; break; + case WSAEDISCON: msg += "Graceful shutdown in progress"; break; + case WSAHOST_NOT_FOUND: msg += "Host not found"; break; + case WSANO_DATA: msg += "No host data of that type was found"; break; + default: msg += "Unknown Winsock error: " + WSAGetLastError(); break; + } + #endif + + int errorno; + + //Win32 braindamage + #ifdef __WIN32__ + errorno = WSAGetLastError(); + #else + errorno = errno; + #endif + + SWBaseError e; + + if( errorno == EADDRINUSE ) + e = portInUse; + else if( errorno == EAGAIN || errorno == EWOULDBLOCK ) + e = notReady; + else if( errorno == EMSGSIZE ) + e = msgTooLong; + else if( errorno == EINPROGRESS || errorno == EALREADY ) + e = notReady; + else if( errorno == ECONNREFUSED || errorno == ETIMEDOUT ) + e = noResponse; + else if( errorno == ENOTCONN || errorno == EBADF || errorno == ENOTSOCK ) + e = notConnected; + else if( errorno == EPIPE ){ + e = terminated; + recv_close = true; + }else if( errorno == EINTR ) + e = interrupted; + else + e = fatal; //default + + set_error(error, e, msg); +} + +void SWBaseSocket::no_error(SWBaseError *error) +{ + if(error != NULL){ + *error = ok; + error->error_string = ""; + error->failed_class = NULL; + } +} + +void SWBaseSocket::set_error(SWBaseError *error, SWBaseError name, string msg) +{ + error_string = msg; + + if(error != NULL){ + *error = name; + error->error_string = msg; + error->failed_class = this; + }else{ + if( sw_Verbose ) + print_error(); + + if( sw_DoThrow ){ + SWBaseError e; + e = name; + e.error_string = msg; + e.failed_class = this; + throw e; + } + } +} + diff --git a/sockets/sw_base.h b/sockets/sw_base.h new file mode 100644 index 00000000..a6a191f1 --- /dev/null +++ b/sockets/sw_base.h @@ -0,0 +1,205 @@ +// C++ Socket Wrapper +// SocketW base socket header +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindström + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#ifndef sw_base_H +#define sw_base_H + +#include "sw_internal.h" + +#include +#include + +// Set error handling mode +// throw_errors == true : Throws the error class on unhandled errors +// throw_errors == false : Exit on unhandled errors +// verbose == true : Prints the error message to stderr on unhandled errors +// +// Default is throw_errors == false and verbose == true +void sw_setThrowMode(bool throw_errors); +void sw_setVerboseMode(bool verbose); +bool sw_getThrowMode(void); +bool sw_getVerboseMode(void); + + +// Abstract base class for streaming sockets +class DECLSPEC SWBaseSocket +{ +public: + SWBaseSocket(); + virtual ~SWBaseSocket(); + + // Error types + // ok - operation succesful + // fatal - unspecified error + // notReady - you should call the function again + // indicates that the function would block (if nowait/nonblocking) + // portInUse - this port is used by another socket ( on listen() ) + // notConnected - socket not connected (or valid) + // msgTooLong - the message size it too big for send() + // terminated - connection terminated (by peer) + // noResponse - can't connect() to peer + // timeout - a read/write operation timed out (only if a timeout value is set and if in blocking mode) + // interrupted - operation was interrupted by a nonblocked signal + enum base_error{ok, fatal, notReady, portInUse, notConnected, msgTooLong, terminated, noResponse, timeout, interrupted}; + + class DECLSPEC SWBaseError + { + public: + SWBaseError(); + SWBaseError(base_error e); + + virtual ~SWBaseError(){;} + + virtual std::string get_error(); + virtual SWBaseSocket* get_failedClass(void); + + virtual bool operator==(SWBaseError e); + virtual bool operator!=(SWBaseError e); + + virtual void set_errorString(std::string msg); + virtual void set_failedClass(SWBaseSocket *pnt); + protected: + friend class SWBaseSocket; + + // The base error type + base_error be; + + // Human readable error string + std::string error_string; + + // A pointer to the class causing the error + SWBaseSocket *failed_class; + }; + + + // Note: If no SWBaseError class is provided with a method call (==NULL), + // SocketW will print the error to stderr and exit or throw on errors. + + // Note: All bool functions returns true on success. + + // Block mode + // blocking - everythings blocks until completly done + // noWait - operations block but only once + // useful with blocking w. select() + // nonblocking - don't block (you should use select()) + enum block_type{blocking, noWait, nonblocking}; + + + // Connection methods + // qLimit - the maximum length the queue of pending connections. + // Accept returns a new socket class connected with peer (should be + // freed with delete) or NULL on failure. You can cast the class to + // the correct type if you need to ( eg. (SWInetSocket *)mysocket ). + virtual bool listen(int qLimit = 5, SWBaseError *error = NULL); + virtual SWBaseSocket* accept(SWBaseError *error = NULL); + // bind() and connect() are implemented in child classes + + // do the disconnect ritual (signal peer, wait for close singal and close socket) + virtual bool disconnect(SWBaseError *error = NULL); + + // force close socket + virtual bool close_fd(); //use with care, disconnect() is cleaner + + // Direct I/O (raw) + // Can send/recv less bytes than specified! + // Returns the actual amount of bytes sent/recv on sucess + // and an negative integer on failure. + virtual int send(const char *buf, int bytes, SWBaseError *error = NULL); + virtual int sendmsg(const std::string msg, SWBaseError *error = NULL); + virtual int recv(char *buf, int bytes, SWBaseError *error = NULL); + virtual std::string recvmsg(int bytes = 256, SWBaseError *error = NULL); + + // Forced I/O + // Force system to send/recv the specified amount of bytes. + // On nowait/nonblocking: might return with notReady and then you + // MUST call the same method again (eg. wait with select() to know when) + // with the same parameters until the operation is finished. + // Returns 'bytes' when finished, negative integer on failure and + // 'notReady'. In the 'notReady' case, -(return value) is the amount of + // bytes sent/recv so far. + virtual int fsend(const char *buf, int bytes, SWBaseError *error = NULL); + virtual int fsendmsg(const std::string msg, SWBaseError *error = NULL); + virtual int frecv(char *buf, int bytes, SWBaseError *error = NULL); + + // Tools + // get_fd() - get socket descriptor, can be used with select() + // returns -1 on failure. + // get_host/peer fills the provided structures with info about the + // host/peer (see man unix & ip). + // SWInetSocket has some more tools for TCP/IP sockets. + virtual int get_fd(SWBaseError *error); + virtual bool get_host(sockaddr *host, SWBaseError *error = NULL); + virtual bool get_peer(sockaddr *peer, SWBaseError *error = NULL); + + // Set recv timeout (only in blocking mode). + // set_timeout(0,0) means wait forever (default). + // This affects the functions recv(), send(), accept() and disconnect() + // and others that use those, i.e. all frecvmsg(). + void set_timeout(Uint32 sec, Uint32 usec){ tsec = sec, tusec = usec; } + + // Error handling + virtual void print_error(); //prints the last error if any to stderr + virtual std::string get_error(){return error_string;} //returns a human readable error string + +protected: + // get a new socket if myfd < 0 + virtual void get_socket()=0; + + // create a new class for accept() using socketdescriptor + virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error)=0; + + // reset state + virtual void reset(); + + // wait for I/O (with timeout) + enum io_type{read, write, except, rw, all}; + virtual bool waitIO(io_type &type, SWBaseError *error); + bool waitRead(SWBaseError *error); + bool waitWrite(SWBaseError *error); + + // internal error handling + virtual void handle_errno(SWBaseError *error, std::string msg); + virtual void no_error(SWBaseError *error); + virtual void set_error(SWBaseError *error, SWBaseError name, std::string msg); + + // our socket descriptor + int myfd; + + // last error + std::string error_string; + + // data for fsend + bool fsend_ready; + int fsend_total; + int fsend_bytesleft; + + // data for frecv + bool frecv_ready; + int frecv_total; + int frecv_bytesleft; + + // have we recived a shutdown signal? + bool recv_close; + + //blocking mode (set by child classes) + block_type block_mode; + + //timeout for waitIO() + int tsec; + int tusec; +}; + + +#endif /* sw_base_H */ diff --git a/sockets/sw_inet.cpp b/sockets/sw_inet.cpp new file mode 100644 index 00000000..c056722e --- /dev/null +++ b/sockets/sw_inet.cpp @@ -0,0 +1,249 @@ +// C++ Socket Wrapper +// SocketW Inet socket +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindström + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#include "sw_inet.h" + +#ifndef __WIN32__ + #include + #include + #include + #include + #include +#else + #define F_SETFL FIONBIO + #define O_NONBLOCK 1 + + //Defined in sw_base.cxx + extern int close(int fd); + extern int fcntl(int fd, int cmd, long arg); +#endif + +using namespace std; + +//==================================================================== +//== SWInetSocket +//== Inet (TCP/IP) streaming sockets +//==================================================================== +SWInetSocket::SWInetSocket(block_type block) +{ + block_mode = block; +} + +SWInetSocket::~SWInetSocket() +{ + +} + +void SWInetSocket::get_socket() +{ + if( myfd < 0 ){ + myfd = socket(PF_INET, SOCK_STREAM, 0); + + if( block_mode == nonblocking ) + fcntl(myfd, F_SETFL, O_NONBLOCK); + + //reset state + reset(); + } +} + +SWBaseSocket* SWInetSocket::create(int socketdescriptor, SWBaseError *error) +{ + SWInetSocket* remoteClass; + + /* Create new class*/ + remoteClass = new SWInetSocket(block_mode); + remoteClass->myfd = socketdescriptor; + + no_error(error); + return remoteClass; +} + +bool SWInetSocket::bind(int port, SWBaseError *error) +{ + return bind(port, "", error); +} + +bool SWInetSocket::bind(int port, string host, SWBaseError *error) +{ + hostent *h; + in_addr inp; + + if( host.size() > 0 ){ + // Bind to a specific address + + if( (h = gethostbyname(host.c_str())) == NULL ){ + set_error(error, fatal, "SWInetSocket::bind() - Can't get host by name"); + return false; + } + + inp = *((in_addr *)h->h_addr); + }else{ + // Bind to any + inp.s_addr = INADDR_ANY; + } + + + get_socket(); + + sockaddr_in myAdr; + + memset(&myAdr, 0, sizeof(myAdr)); + myAdr.sin_family = AF_INET; + myAdr.sin_port = htons(port); + myAdr.sin_addr.s_addr = inp.s_addr; + + if(::bind(myfd, (sockaddr *)&myAdr, sizeof(myAdr)) == -1){ + handle_errno(error, "SWInetSocket::bind() error: "); + return false; + } + + no_error(error); + return true; +} + +bool SWInetSocket::connect(int port, string hostname, SWBaseError *error) +{ + get_socket(); + + hostent *host; + + if( (host = gethostbyname(hostname.c_str())) == NULL ){ + set_error(error, fatal, "SWInetSocket::connect() - Can't get host by name"); + return false; + } + + sockaddr_in remoteAdr; + + memset(&remoteAdr, 0, sizeof(remoteAdr)); + remoteAdr.sin_family = AF_INET; + remoteAdr.sin_port = htons(port); + remoteAdr.sin_addr = *((in_addr *)host->h_addr); + + if(::connect(myfd, (sockaddr *)&remoteAdr, sizeof(remoteAdr)) == -1){ + handle_errno(error, "SWInetSocket::connect() error: "); + return false; + } + + no_error(error); + return true; +} + + +string SWInetSocket::get_peerAddr(SWBaseError *error) +{ + sockaddr_in adr; + + if( !get_peer((sockaddr *)&adr, error) ) + return ""; + + char *pnt; + + if( (pnt = inet_ntoa(adr.sin_addr)) == NULL ){ + set_error(error, fatal, "SWInetSocket::get_peerName() - Can't get peer address"); + return ""; + } + string name(pnt); + + no_error(error); + return name; +} + +int SWInetSocket::get_peerPort(SWBaseError *error) +{ + sockaddr_in adr; + + if( !get_peer((sockaddr *)&adr, error) ) + return -1; + + no_error(error); + + return ntohs(adr.sin_port); +} + +string SWInetSocket::get_peerName(SWBaseError *error) +{ + string name = get_peerAddr(error); + if(name.size() < 1) + return ""; + + + hostent *h; + + if( (h = gethostbyname(name.c_str())) == NULL ){ + set_error(error, fatal, "SWInetSocket::get_peerName() - Can't get peer by address"); + return ""; + } + string host_name(h->h_name); + + no_error(error); + return host_name; +} + +string SWInetSocket::get_hostAddr(SWBaseError *error) +{ + //We need to get the real address, so we must + //first get this computers host name and then + //translate that into an address! + + string name = get_hostName(error); + if( name.size() < 1 ) + return ""; + + hostent *host; + + if( (host = gethostbyname(name.c_str())) == NULL ){ + set_error(error, fatal, "SWInetSocket::get_hostAddr() - Can't get host by name"); + return ""; + } + + char *pnt; + + if( (pnt = inet_ntoa(*((in_addr *)host->h_addr))) == NULL){ + set_error(error, fatal, "SWInetSocket::get_hostAddr() - Can't get host address"); + return ""; + } + + string adr(pnt); + + return adr; +} + +int SWInetSocket::get_hostPort(SWBaseError *error) +{ + sockaddr_in adr; + + if( !get_host((sockaddr *)&adr, error) ) + return -1; + + no_error(error); + + return ntohs(adr.sin_port); +} + +string SWInetSocket::get_hostName(SWBaseError *error) +{ + char buf[256]; + + if( gethostname(buf, 256) != 0 ){ + handle_errno(error, "SWInetSocket::gethostname() error: "); + return ""; + } + + string msg(buf); + + no_error(error); + return msg; +} diff --git a/sockets/sw_inet.h b/sockets/sw_inet.h new file mode 100644 index 00000000..26050845 --- /dev/null +++ b/sockets/sw_inet.h @@ -0,0 +1,48 @@ +// C++ Socket Wrapper +// SocketW Inet socket header +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindström + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#ifndef sw_inet_H +#define sw_inet_H + +#include "sw_internal.h" +#include "sw_base.h" +#include + +// Simple streaming TCP/IP class +class DECLSPEC SWInetSocket : public SWBaseSocket +{ +public: + SWInetSocket(block_type block=blocking); + virtual ~SWInetSocket(); + + virtual bool bind(int port, SWBaseError *error = NULL); //use port=0 to get any free port + virtual bool bind(int port, std::string host, SWBaseError *error = NULL); //you can also specify the host interface to use + virtual bool connect(int port, std::string hostname, SWBaseError *error = NULL); + + // Tools + // Gets IP addr, name or port. + virtual std::string get_peerAddr(SWBaseError *error = NULL); + virtual int get_peerPort(SWBaseError *error = NULL); + virtual std::string get_peerName(SWBaseError *error = NULL); + virtual std::string get_hostAddr(SWBaseError *error = NULL); + virtual int get_hostPort(SWBaseError *error = NULL); + virtual std::string get_hostName(SWBaseError *error = NULL); + +protected: + virtual void get_socket(); + virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error); +}; + +#endif /* sw_inet_H */ diff --git a/sockets/sw_internal.h b/sockets/sw_internal.h new file mode 100644 index 00000000..94313bcd --- /dev/null +++ b/sockets/sw_internal.h @@ -0,0 +1,75 @@ +// C++ Socket Wrapper +// SocketW internal header +// +// Started 030823 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindstr�m + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#ifndef sw_internal_H +#define sw_internal_H + +// This header is included in all *.h files + +#ifndef __WIN32__ + #include + #include + #include + #include +#else + #include + + #define F_SETFL FIONBIO + #define O_NONBLOCK 1 +#endif + +#ifndef _SDL_H + +// Define general types +typedef unsigned char Uint8; +typedef signed char Sint8; +typedef unsigned short Uint16; +typedef signed short Sint16; +typedef unsigned int Uint32; +typedef signed int Sint32; + +// It's VERY important that these types really have the right sizes! +// This black magic is from SDL +#define COMPILE_TIME_ASSERT(name, x) \ + typedef int _dummy_ ## name[(x) * 2 - 1] +COMPILE_TIME_ASSERT(uint8, sizeof(Uint8) == 1); +COMPILE_TIME_ASSERT(sint8, sizeof(Sint8) == 1); +COMPILE_TIME_ASSERT(uint16, sizeof(Uint16) == 2); +COMPILE_TIME_ASSERT(sint16, sizeof(Sint16) == 2); +COMPILE_TIME_ASSERT(uint32, sizeof(Uint32) == 4); +COMPILE_TIME_ASSERT(sint32, sizeof(Sint32) == 4); +#undef COMPILE_TIME_ASSERT + +#endif /* _SDL_H */ + +// Some compilers use a special export keyword +#ifndef DECLSPEC + #ifdef __BEOS__ + #if defined(__GNUC__) + #define DECLSPEC __declspec(dllexport) + #else + #define DECLSPEC __declspec(export) + #endif + #else + #ifdef WIN32 + #define DECLSPEC __declspec(dllexport) + #else + #define DECLSPEC + #endif + #endif +#endif + + +#endif /* sw_internal_H */ diff --git a/sockets/sw_unix.cpp b/sockets/sw_unix.cpp new file mode 100644 index 00000000..07e2dd10 --- /dev/null +++ b/sockets/sw_unix.cpp @@ -0,0 +1,99 @@ +// C++ Socket Wrapper +// SocketW Unix socket +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindström + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#include "sw_unix.h" +#include + +using namespace std; + +//==================================================================== +//== SWUnixSocket +//== Unix streaming sockets +//==================================================================== +#ifndef __WIN32__ + +SWUnixSocket::SWUnixSocket(block_type block) +{ + block_mode = block; +} + +SWUnixSocket::~SWUnixSocket() +{ + //nothing here +} + +void SWUnixSocket::get_socket() +{ + if( myfd < 0 ){ + myfd = socket(PF_UNIX, SOCK_STREAM, 0); + + if( block_mode == nonblocking ) + fcntl(myfd, F_SETFL, O_NONBLOCK); + + //reset state + reset(); + } +} + + +SWBaseSocket* SWUnixSocket::create(int socketdescriptor, SWBaseError *error) +{ + SWUnixSocket* remoteClass; + + /* Create new class*/ + remoteClass = new SWUnixSocket(block_mode); + remoteClass->myfd = socketdescriptor; + + no_error(error); + return remoteClass; +} + +bool SWUnixSocket::bind(string path, SWBaseError *error) +{ + get_socket(); + + sockaddr_un myAdr; + + myAdr.sun_family = AF_UNIX; + strncpy(myAdr.sun_path, path.c_str(), path.size()+1); + + if(::bind(myfd, (sockaddr *)&myAdr, sizeof(myAdr)) == -1){ + handle_errno(error, "SWUnixSocket::bind() error: "); + return false; + } + + no_error(error); + return true; +} + +bool SWUnixSocket::connect(string path, SWBaseError *error) +{ + get_socket(); + + sockaddr_un remoteAdr; + + remoteAdr.sun_family = AF_UNIX; + strncpy(remoteAdr.sun_path, path.c_str(), path.size()+1); + + if(::connect(myfd, (sockaddr *)&remoteAdr, sizeof(remoteAdr)) == -1){ + handle_errno(error, "SWUnixSocket::connect() error: "); + return false; + } + + no_error(error); + return true; +} + +#endif /* __WIN32__ */ diff --git a/sockets/sw_unix.h b/sockets/sw_unix.h new file mode 100644 index 00000000..576f14c0 --- /dev/null +++ b/sockets/sw_unix.h @@ -0,0 +1,41 @@ +// C++ Socket Wrapper +// SocketW Unix socket header +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindström + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#ifndef sw_unix_H +#define sw_unix_H +#ifndef __WIN32__ + +#include "sw_internal.h" +#include "sw_base.h" +#include + +// Simple streaming Unix class +class DECLSPEC SWUnixSocket : public SWBaseSocket +{ +public: + SWUnixSocket(block_type block=blocking); + ~SWUnixSocket(); + + // bind and connect to the socket file "path" + virtual bool bind(std::string path, SWBaseError *error = NULL); + virtual bool connect(std::string path, SWBaseError *error = NULL); + +protected: + virtual void get_socket(); + virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error); +}; + +#endif /* __WIN32__ */ +#endif /* sw_unix_H */ diff --git a/util/flv.cpp b/util/flv.cpp new file mode 100644 index 00000000..f374c8d5 --- /dev/null +++ b/util/flv.cpp @@ -0,0 +1,69 @@ +#include //for read() + +struct FLV_Pack { + int len; + int buf; + char * data; +};//FLV_Pack + +char FLVHeader[13]; + +//reads full length from a file descriptor +void Magic_Read(char * buf, int len, int file){ + int i = 0; + while (i < len) i += read(file, buf, len-i); +} + + +//reads a FLV header and checks for correctness +//returns true if everything is alright, false otherwise +bool FLV_Readheader(int file){ + Magic_Read(FLVHeader,13,file); + if (FLVHeader[0] != 'F') return false; + if (FLVHeader[1] != 'L') return false; + if (FLVHeader[2] != 'V') return false; + if (FLVHeader[8] != 0x09) return false; + if (FLVHeader[9] != 0) return false; + if (FLVHeader[10] != 0) return false; + if (FLVHeader[11] != 0) return false; + if (FLVHeader[12] != 0) return false; + return true; +}//FLV_Readheader +bool FLV_Readheader(){ + fread(FLVHeader,1,13,stdin); + if (FLVHeader[0] != 'F') return false; + if (FLVHeader[1] != 'L') return false; + if (FLVHeader[2] != 'V') return false; + if (FLVHeader[8] != 0x09) return false; + if (FLVHeader[9] != 0) return false; + if (FLVHeader[10] != 0) return false; + if (FLVHeader[11] != 0) return false; + if (FLVHeader[12] != 0) return false; + return true; +}//FLV_Readheader + +//gets a packet, storing in given FLV_Pack pointer. +//will assign pointer if null +//resizes FLV_Pack data field bigger if data doesn't fit +// (does not auto-shrink for speed!) +void FLV_GetPacket(FLV_Pack *& p, int file){ + if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} + if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} + Magic_Read(p->data,11,file); + p->len = p->data[3] + 15; + p->len += (p->data[2] << 8); + p->len += (p->data[1] << 16); + if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);} + Magic_Read(p->data+11,p->len-11,file); +}//FLV_GetPacket + +void FLV_GetPacket(FLV_Pack *& p){ + if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} + if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} + fread(p->data,1,11,stdin); + p->len = p->data[3] + 15; + p->len += (p->data[2] << 8); + p->len += (p->data[1] << 16); + if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);} + fread(p->data+11,1,p->len-11,stdin); +}//FLV_GetPacket