diff --git a/include/netsock.cpp b/include/netsock.cpp new file mode 100755 index 000000000..638825edd --- /dev/null +++ b/include/netsock.cpp @@ -0,0 +1,603 @@ +#include +#include + +// skstream.h +// Copyright (C) 1995, 1996 by John C. Wang. All Rights Reserved. +// +// You may distribute this file with your product in either of two ways: +// IN SOURCE CODE FORM: You must include this file in its entirety. +// IN OBJECT CODE FORM: You must give proper acknowledgements to the author(s) +// of this program. This may take the form of credits on the start-up screen. +// +// IN ANYCASE, THIS SOFTWARE IS DISTRIBUTED WITHOUT ANY KIND OF EXPLICIT OR +// IMPLICIT WARRANTIES AND THE AUTHORS ARE NOT RESPONSIBLE FOR ANY EVENTS THAT +// OCCURS AS A RESULT OF USING THIS SOFTWARE. +// +// History: +// [JCW 95-Dec-04] created +// [JCW 95-Dec-20] comments added for distribution 95a +// [JCW 96-Jan-01] removed UDP capabilities from skstream + +#include + +#include "winsock.h" + +// +// sockbuf +// +class sockbuf : public streambuf +{ +public: + sockbuf( SOCKET & ); + sockbuf( SOCKET &, char *, int ); + + virtual ~sockbuf(); + + virtual int overflow(int =EOF) ; + virtual int underflow() ; + + virtual int sync(); + +protected: + char *_buffer ; + + // sockbuf specific + SOCKET &_socket ; +} ; + + +// +// skstream +// +class skstream : public iostream { +public: + // constants + enum service + { + ftp = 21, //tcp + telnet = 23, //tcp + smtp = 25, //tcp mail + time = 37, //tcp timserver + name = 42, //tcp nameserver + nameserver = 53, //tcp domain # name-domain server + finger = 79, //tcp + http = 80, //tcp + pop = 109, //tcp postoffice + pop2 = 109, //tcp # Post Office + pop3 = 110, //tcp postoffice + nntp = 119 //tcp usenet # Network News Transfer + } ; + + enum role + { + server , + client + } ; + + // methods + skstream( void ); + skstream( const char *addr, const service, const role = client ) ; + skstream( SOCKET ); + ~skstream( void ); + + void open( const char *addr, const service, const role = client ) ; + void close( void ) ; + int is_open( void ) const ; + void attach( SOCKET = NULL ); + SOCKET getsocket() const ; + + char *getpeername( char *, int ) const ; + unsigned short getport( void ) const ; +protected: + SOCKET _socket ; + sockbuf _sockbuf ; + + // platform dependent library housekeeping + int init( void ) ; + int shutdown( void ) ; +}; + +// skstream.cpp +// Copyright (C) 1996, 1995 by John C. Wang +// All Rights Reserved. +// +// You may distribute this file with your product in either of two ways: +// IN SOURCE CODE FORM: You must include this file in its entirety. +// IN OBJECT CODE FORM: You must give proper acknowledgements to the author(s) +// of this program. This may take the form of credits on the start-up screen. +// +// IN ANYCASE, THIS SOFTWARE IS DISTRIBUTED WITHOUT ANY KIND OF EXPLICIT OR +// IMPLICIT WARRANTY AND THE AUTHOR(S) ARE NOT RESPONSIBLE FOR ANY EVENT THAT +// OCCURS DUE TO THE USE OR MISUSE OF THIS SOFTWARE. +// +// History: +// [JCW 95-Dec-04] created. +// [JCW 95-Dec-20] comments added for distribution 95a. +// [JCW 96-Jan-01] removed UDP capabilities from skstream. +// [JCW 96-Mar-14] exceptions made optional. +// [JCW 96-Oct-20] protected skstream::init and ::shutdown changed. +// char( 255 ) == EOF bug fixed. + +#include + +#ifdef _UNIX +#define INVALID_SOCKET -1 +// Add definitions here +#endif + +// Machine independent macros +#ifdef DBG + #define dassert(x) assert(x) + #define debug(x) x +#else + #define dassert(x) + #define debug(x) +#endif //def _DEBUG + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE !FALSE +#endif + +// +// skstream +// + +int skstream::init( void ) +{ + // platform dependent initialization + const WORD wMinVer = 0x0101; // request WinSock v1.1 (at least) + WSADATA wsaData; + if( 0 == WSAStartup( wMinVer, &wsaData ) ) + return TRUE ; + return FALSE ; +} + +int skstream::shutdown( void ) +{ + // platform dependent shutdown + if( 0 == WSACleanup() ) + return TRUE ; + return FALSE ; +} + +skstream::skstream( void ) : _sockbuf( _socket ), iostream( &_sockbuf ) +{ + if( init() ) + attach( INVALID_SOCKET ) ; +} + +skstream::skstream( const char *addr, const service port, const role side ) + : _sockbuf( _socket ), iostream( &_sockbuf ) +{ + if( init() ) + { + attach( INVALID_SOCKET ) ; + open( addr, port, side ) ; + } +} + +skstream::skstream( SOCKET sock ) : _sockbuf( _socket ), iostream( &_sockbuf ) +{ + if( init() ) + attach( sock ) ; +} + +skstream::~skstream( void ) +{ + close() ; + shutdown() ; +} + +int skstream::is_open( void ) const +{ + return ( INVALID_SOCKET != getsocket() ) ; +} + +void skstream::open( const char *addr, const service port, const role side ) +{ + // in case of error condition, user finds out by testing is_open() + + if( is_open() ) + close() ; + + // 1. Create socket + if( INVALID_SOCKET == ( _socket = ::socket( PF_INET, SOCK_STREAM, 0 ) ) ) + // Cannot create socket + return ; + + // 2. Bind + SOCKADDR_IN sa ; + sa.sin_family = AF_INET ; + sa.sin_addr.S_un.S_addr = INADDR_ANY ; + sa.sin_port = side == client ? 0 : htons( (unsigned short)port ) ; + // rationale: no client requires fixed port number, so let system assign + if( SOCKET_ERROR == ::bind( _socket, (sockaddr *)&sa, sizeof( sa ) ) ) + { + // Cannot bind to the chosen port + close() ; + return ; + } + + // From now on the two sides are very much different + if( side == skstream::client ) + { + // 3(cli). Connect + SOCKADDR_IN sa ; + sa.sin_family = AF_INET ; + + hostent *he = NULL; + + int ip[4]; + if (sscanf( addr, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]) == 4) + { + char ipaddr[4]; + for (int i = 0; i < 4; i++) + ipaddr[i] = (char)ip[i]; + + he = ::gethostbyaddr( ipaddr, 4, PF_INET ); + } + else + he = ::gethostbyname( addr ); + + if( NULL == he ) + { + // Cannot resolve remote server ip + close() ; + return ; + } + + sa.sin_addr.S_un.S_addr = *(unsigned long *)( he->h_addr_list[ 0 ] ) ; + sa.sin_port = htons( port ) ; + + if( SOCKET_ERROR == ::connect( _socket, (sockaddr *)&sa, sizeof( sa ) ) ) + { + // Connection error + close() ; + return ; + } + } + else + { + // 3(svr). Listen + if( SOCKET_ERROR == ::listen( _socket, 5 ) ) // max backlog + { + // Error listening + close() ; + return ; + } + + // 4. Accept + SOCKET commsock ; + if( INVALID_SOCKET == ( commsock = ::accept( _socket, NULL, NULL ) ) ) + { + // Accepting error + close() ; + return ; + } + + // rationale: after we accept a connection, close the server port + // so another process may become a server + if( SOCKET_ERROR == ::closesocket( _socket ) ) + { + // Cannot close service port, resource may be occupied + close() ; + return ; + } + _socket = commsock ; + } + // success! +} + +void skstream::close( void ) +{ + if( is_open() ) + { + _sockbuf.sync() ; + if( SOCKET_ERROR == ::closesocket( _socket ) ) + // Cannot close. Leave _socket as it was + return ; + } + + _socket = INVALID_SOCKET ; +} + +void skstream::attach( SOCKET sock ) +{ + _socket = sock ; +} + +SOCKET skstream::getsocket() const +{ + return _socket ; +} + +char *skstream::getpeername( char *buf, int size ) const +{ + SOCKADDR_IN sa ; + int sasize = sizeof( sa ) ; + if( SOCKET_ERROR ==::getpeername( getsocket(), (sockaddr *)&sa, &sasize ) ) + // Cannot get peer name + return NULL ; + strncpy( buf, ::inet_ntoa( sa.sin_addr ), size - 1 ) ; + return buf ; +} + +unsigned short skstream::getport( void ) const +{ + SOCKADDR_IN sa ; + int sasize = sizeof( sa ) ; + if( SOCKET_ERROR ==::getpeername( getsocket(), (sockaddr *)&sa, &sasize ) ) + // Cannot get peer port + return ntohs( IPPORT_RESERVED ) ; + return ntohs( sa.sin_port ) ; +} + +// +// sockbuf +// +sockbuf::sockbuf( SOCKET &sock ) : _socket( sock ), streambuf( NULL, 0 ) +{ + const int insize = 0x2000 ; // allocate 16k buffer each for input and output + const int outsize = 0x2000 ; + const int bufsize = insize + outsize ; + + _buffer = new char [ bufsize ] ; + + if( this != setbuf( _buffer, bufsize ) ) + _buffer = NULL ; + else + { + setp( _buffer, _buffer + insize ) ; + setg( _buffer + insize, _buffer + bufsize, _buffer + bufsize ) ; + } +} + +sockbuf::sockbuf( SOCKET &sock, char *buf, int length ) + : _socket( sock ), streambuf( buf, length ) +{ + _buffer = NULL ; + + setp( buf, buf + length / 2 ) ; + setg( buf + length / 2, buf + length, buf + length ) ; +} + +sockbuf::~sockbuf() +{ + delete[] _buffer ; + _buffer = NULL ; +} + +int sockbuf::overflow( int nCh ) +{ + // in case of error, user finds out by testing fail() + if( _socket == INVALID_SOCKET ) + // Invalid socket + return EOF ; + + if( pptr() - pbase() <= 0 ) + // nothing to send + return 0 ; + + int size ; + if( SOCKET_ERROR == ( size = ::send( _socket, pbase(), pptr() - pbase(), 0 ) ) ) + // (TCP) Cannot send + return EOF ; + + if( size == 0 ) + // remote site has closed this connection + return EOF ; + + if( nCh != EOF ) // size >= 1 at this point + { + size-- ; + *( pbase() + size ) = nCh ; + } + + // move remaining pbase() + size .. pptr() - 1 => pbase() .. pptr() - size - 1 + for( char *p = pbase() + size; p < pptr(); p++ ) + *( p - size ) = *p ; + const int newlen = ( pptr() - pbase() ) - size ; + + setp( pbase(), epptr() ) ; + pbump( newlen ) ; + + return 0 ; +} + +int sockbuf::underflow() +{ + // if get area not empty, return first character + // else fill up get area and return 1st character + + // in case of error, user finds out by testing eof() + + if( _socket == INVALID_SOCKET ) + // Invalid socket! + return EOF ; + + if( egptr() - gptr() > 0 ) + return (int)(unsigned char)(*gptr()) ; + + // fill up from eback to egptr + int size ; + if( SOCKET_ERROR == ( size = ::recv( _socket, eback(), egptr() - eback(), 0 ) ) ) + // (TCP) Receive error + return EOF ; + + if( size == 0 ) + // remote site has closed this connection + return EOF ; + + // move rcvd data from eback() .. eback() + size to egptr() - size .. egptr() + const int delta = egptr() - ( eback() + size ) ; + for( char *p = eback() + size - 1; p >= eback(); p-- ) + { + CHECK( p + delta >= eback(), "Socket underflow" ) ; + CHECK( p + delta < egptr(), "Socket underflow" ) ; + *( p + delta ) = *p ; + } + + setg( eback(), egptr() - size, egptr() ) ; + + return (int)(unsigned char)(*gptr()) ; +} + +int sockbuf::sync() +{ + if( EOF == overflow() ) + return EOF ; // ios will set the fail bit + else + { + // empty put and get areas + setp( pbase(), epptr() ) ; + setg( eback(), egptr(), egptr() ) ; + + return 0 ; + } +} + +/////////////////////////////////////////////////////////// +// end of skstreams +/////////////////////////////////////////////////////////// + +#define NO_MFC +#define CObject TObject +#define CString TString +#define CStringArray TString_array +#include "NetSock.h" + +#define STRICT +#include + + +static TLanManager* pSocketManager = NULL; +static skstream* cur_socket = NULL; + +TSocketClient::TSocketClient() + : m_pData(NULL), m_dwSize(0) +{ } + +TSocketClient::~TSocketClient() +{ + ReleaseBuffer(); + RemoveAllConnections(); + ShutDown(); +} + +BOOL TSocketClient::Boot() +{ + return TRUE; +} + +BOOL TSocketClient::ShutDown() +{ + pSocketManager = NULL; + if (cur_socket) + { + delete cur_socket; + cur_socket = NULL; + } + return TRUE; +} + + +BOOL TSocketClient::OnRemoveConnection(DWORD id) +{ + if (cur_socket) + { + delete cur_socket; + cur_socket = NULL; + } + return TRUE; +} + +TConnection* TSocketClient::OnQueryConnection(const char* service, + const char* server) +{ + if (pSocketManager == NULL) + Boot(); + + TConnection* pConnection = NULL; + + if (cur_socket == NULL) + { + TString strServer = server; + if (strServer.empty()) + strServer = "127.0.0.1"; + + cur_socket = new skstream(strServer, (skstream::service)atoi(service)); + + if (cur_socket->is_open()) + pConnection = OnCreateConnection((DWORD)cur_socket); + else + { + delete cur_socket; + cur_socket = NULL; + } + } + + return pConnection; +} + +BOOL TSocketClient::Execute(DWORD id, const char* cmd) +{ + BOOL ok = cur_socket->is_open(); + if (ok) + { + const int buflen = strlen(cmd)+1; + cur_socket->sync(); + cur_socket->write(cmd, buflen); + cur_socket->flush(); + ok = cur_socket->good(); + } + return ok; +} + +BOOL TSocketClient::Request(DWORD id, const char* cmd) +{ + static BOOL semaphore = FALSE; + BOOL ok = FALSE; + + if (!semaphore) + { + semaphore = TRUE; + + ReleaseBuffer(); + ok = Execute(id, cmd); + if (ok) + { + m_dwSize = 0; + cur_socket->read((char*)&m_dwSize, sizeof(m_dwSize)); + ok = m_dwSize > 0; + if (ok) + { + m_pData = new BYTE[m_dwSize]; + cur_socket->read(m_pData, (int)m_dwSize); + ok = cur_socket->good(); + } + } + + semaphore = FALSE; + } + return ok; +} + +BYTE* TSocketClient::GetBuffer(DWORD& dwSize) +{ + dwSize = m_dwSize; + return m_pData; +} + +void TSocketClient::ReleaseBuffer() +{ + if (m_pData) + { + delete m_pData; + m_pData = NULL; + m_dwSize = 0; + } +} diff --git a/include/netsock.h b/include/netsock.h new file mode 100755 index 000000000..12c1c8565 --- /dev/null +++ b/include/netsock.h @@ -0,0 +1,61 @@ +#ifndef __NETSOCK_H__ +#define __NETSOCK_H__ + +#ifndef __NETUTILS_H__ +#include "NetUtils.h" +#endif + +class TConnection; + +class TSocketServer : public TLanServer +{ + BYTE* m_pData; + DWORD m_dwSize; + +protected: // TLanManager + virtual BOOL Boot(); + virtual BOOL ShutDown(); + + virtual BOOL OnRemoveConnection(DWORD id); + +public: + BYTE* GetBuffer(DWORD dwSize); + const BYTE* GetData() const { return m_pData; } + + BOOL OnConnect(const CString& topic); + void OnConnectConfirm(DWORD id); + +public: + virtual BOOL IsOk() const; + + TSocketServer(const char* name); + virtual ~TSocketServer(); +}; + +class TSocketClient : public TLanClient +{ + BYTE* m_pData; + DWORD m_dwSize; + +protected: // TLanManager + virtual BOOL Boot(); + virtual BOOL ShutDown(); + virtual OnRemoveConnection(DWORD id); + +protected: // TLanClient + virtual TConnection* OnQueryConnection(const char* service, + const char* server); + +public: + virtual BOOL Execute(DWORD id, const char* cmd); + virtual BOOL Request(DWORD id, const char* cmd); + virtual BYTE* GetBuffer(DWORD& dwSize); + virtual void ReleaseBuffer(); + virtual BOOL IsOk() const { return TRUE; } + + TSocketClient(); + virtual ~TSocketClient(); +}; + +#endif +