diff --git a/BufferCore.h b/BufferCore.h index 6863b12..d0142bc 100644 --- a/BufferCore.h +++ b/BufferCore.h @@ -55,8 +55,8 @@ public: bool FindChar( char SearchChar, int &FoundPos, int StartPos = 0 ); // Miscellaneous - int Size() { return BufSize; }; - int Len() { return BufLen; }; + inline int Size() { return BufSize; }; + inline int Len() { return BufLen; }; }; //--------------------------------------------------------------------------- diff --git a/SelectCore.cpp b/SelectCore.cpp index 2256c13..53a73fa 100644 --- a/SelectCore.cpp +++ b/SelectCore.cpp @@ -8,24 +8,40 @@ // redA Libraries #include "TimingCore.h" #include +#include #include #include #include "SelectableCore.h" //--------------------------------------------------------------------------- -// Select Variables -fd_set ReadTestFDS; -fd_set WriteTestFDS; -fd_set ReadFDS; -fd_set WriteFDS; -int MaxFD = 0; -timeval SelectTime; +// Create Select +CSelect::CSelect( long SelectTimeout ) +{ + // Clear List + FirstHandle = NULL; + // Clear Select sets + FD_ZERO( &ReadTestFDS ); + FD_ZERO( &WriteTestFDS ); + + // Reset maximum File Descriptor + MaxFD = 0; + + // Set Timeout + SetInterval( &Timeout, SelectTimeout ); +} +//--------------------------------------------------------------------------- + +// Destroy Select +CSelect::~CSelect() +{ + return; +} //--------------------------------------------------------------------------- // Clear Select File Descriptors -void SelectClear() +void CSelect::Clear() { // Clear Select sets FD_ZERO( &ReadTestFDS ); @@ -36,24 +52,39 @@ void SelectClear() } //--------------------------------------------------------------------------- -// Set Select timeout -void SelectConfig( long SelectTimeout ) -{ - // Set Timeout - SetInterval( &SelectTime, SelectTimeout ); -} -//--------------------------------------------------------------------------- - // Add Select File Descriptor -void SelectAdd( int FD, bool Read, bool Write ) +void CSelect::Add( int FD, bool Read, bool Write, CSelectableCore * Function ) { + TSelectHandle ** Handle = NULL; + + // Check if Handle already exists + Handle = &FirstHandle; + while (*Handle && ((*Handle)->FD != FD)) { + Handle = &((*Handle)->Next); + } + + // Create if not exist + if (!*Handle) { + // Create + *Handle = (TSelectHandle*)malloc( sizeof(TSelectHandle) ); + memset( *Handle, 0, sizeof(TSelectHandle) ); + + // Set Parameters + (*Handle)->FD = FD; + (*Handle)->Function = Function; + } + // Add Read select - if (Read) + if (Read) { + (*Handle)->Read = true; FD_SET( FD, &ReadTestFDS ); + } // Add Write Select - if (Write) + if (Write) { + (*Handle)->Write = true; FD_SET( FD, &WriteTestFDS ); + } // Check Maximum File Handle if (MaxFD <= FD) @@ -61,60 +92,90 @@ void SelectAdd( int FD, bool Read, bool Write ) } //--------------------------------------------------------------------------- -void SelectRemove( int FD, bool Read, bool Write ) +void CSelect::Remove( int FD, bool Read, bool Write ) { - int TestFD = 0; + TSelectHandle ** Handle = NULL; + TSelectHandle * NextHandle = NULL; + int TestFD = 0; + + // Check if Handle already exists + Handle = &FirstHandle; + while (*Handle && ((*Handle)->FD != FD)) { + Handle = &((*Handle)->Next); + } + // Check if found + if (!*Handle) + return; // Remove from set for select read check - if (Read) + if (Read) { + (*Handle)->Read = false; FD_CLR( FD, &ReadTestFDS); + } // Remove from set for select write check - if (Write) + if (Write) { + (*Handle)->Write = false; FD_CLR( FD, &WriteTestFDS); + } - // Check Maximum file handle - if (FD == MaxFD-1) { - for (TestFD = MaxFD-1; TestFD >= 0; TestFD--) { - if (FD_ISSET( TestFD, &ReadTestFDS ) || FD_ISSET( TestFD, &WriteTestFDS )) { - break; + // Check if to remove from list + if (!(*Handle)->Read && !(*Handle)->Write) + { + // Remove from list + NextHandle = (*Handle)->Next; + free( *Handle ); + *Handle = NextHandle; + + // Update Maximum Test FD + if (FD == MaxFD-1) { + for (TestFD = MaxFD-1; TestFD >= 0; TestFD--) { + if (FD_ISSET( TestFD, &ReadTestFDS ) || FD_ISSET( TestFD, &WriteTestFDS )) { + break; + } } + MaxFD = TestFD+1; } - MaxFD = TestFD+1; } } //--------------------------------------------------------------------------- -bool SelectTest() +bool CSelect::Test() { - int Result = 0; + TSelectHandle * Handle = NULL; + int Events = 0; // Set Test sets ReadFDS = ReadTestFDS; WriteFDS = WriteTestFDS; // Perform select - Result = select( MaxFD, &ReadFDS, &WriteFDS, (fd_set*)NULL, &SelectTime ); - if (Result < 0) + Events = select( MaxFD, &ReadFDS, &WriteFDS, (fd_set*)NULL, &Timeout ); + if (Events < 0) { printf( "Select operation failed (%s)\n", strerror(errno) ); return false; } + // Check all descriptors for events + Handle = FirstHandle; + while (Handle) + { + // Check read Event + if (FD_ISSET( Handle->FD, &ReadFDS ) && Handle->Function) { + Handle->Function->Read( Handle->FD ); + } + + // Check Write Event + if (FD_ISSET( Handle->FD, &WriteFDS ) && Handle->Function) { + Handle->Function->Write( Handle->FD ); + } + + // Next + Handle = Handle->Next; + } + // return success - return (bool)Result; -} -//--------------------------------------------------------------------------- - -// Add Select File Descriptor -bool SelectCheck( int FD, bool &Read, bool &Write ) -{ - // Add Read select - Read = (bool)(FD_ISSET( FD, &ReadFDS )); - - // Add Write Select - Write = (bool)(FD_ISSET( FD, &WriteFDS )); - - return (Read || Write); + return (bool)Events; } //--------------------------------------------------------------------------- diff --git a/SelectableCore.cpp b/SelectableCore.cpp index edc6bf9..f028b88 100644 --- a/SelectableCore.cpp +++ b/SelectableCore.cpp @@ -31,11 +31,14 @@ //--------------------------------------------------------------------------- -CSelectableCore::CSelectableCore( const char * Name ) : +CSelectableCore::CSelectableCore( const char * Name, CSelect * Selector ) : CFunctionCore( Name ) { // Handles FirstHandle = NULL; + + // Select + Select = Selector; } //--------------------------------------------------------------------------- @@ -86,7 +89,7 @@ bool CSelectableCore::RemoveHandle( THandle * Handle ) THandle ** HandlePtr = NULL; // Validate - if (!Handle || (Handle->Type != ctRemoteClient)) { + if (!Handle || (Handle->State == csOpen) || (Handle->State == csWaitingtoOpen)) { return false; } @@ -146,7 +149,7 @@ bool CSelectableCore::SetPortHandle( const char * HandleName, const char * FileN } // Search for file handle - if (!(Handle = GetHandle( HandleName ))) { + if (!(Handle = GetHandle( HandleName )) || (Handle->Type != ctNone)) { return false; } @@ -171,20 +174,15 @@ bool CSelectableCore::SetSocketHandle( const char * HandleName, EConnectType Ty THandle * Handle = NULL; // Validate - if (!Address || (Type == ctNone)) { + if (!Address || (Type == ctNone) || (Type == ctPort)) { return false; } // Search for file handle - if (!(Handle = GetHandle( HandleName ))) { + if (!(Handle = GetHandle( HandleName )) || (Handle->Type != ctNone)) { return false; } - // Clear File Name - if (Handle->FileName) { - free( Handle->FileName ); - } - // Set Type Handle->Type = Type; Handle->KeepAlive = KeepAlive; @@ -292,6 +290,7 @@ int CSelectableCore::OpenPort( THandle * Handle ) } // Confirm open + Handle->State = csOpen; printf( "Port: %s -> Port opened\n", Handle->Name ); return Handle->FD; } @@ -354,6 +353,10 @@ int CSelectableCore::OpenServerSocket( THandle * Handle ) return -1; } + // Set non-blocking flag + int flags = fcntl( Handle->FD, F_GETFL, 0 ); + fcntl( Handle->FD, F_SETFL, flags | O_NONBLOCK ); + // Bind socket if (bind( Handle->FD, (struct sockaddr *)&address, addr_len ) < 0) { @@ -383,14 +386,15 @@ int CSelectableCore::OpenRemoteClientSocket( THandle * Handle ) { THandle ** RemoteClient; int ClientFD; - char ClientAddress[25]; + char ClientAddress[100]; + char ClientName[100]; socklen_t addr_len; struct sockaddr_in address; // Validate if (!Handle) { - return false; + return -1; } // Check Handle type @@ -400,14 +404,17 @@ int CSelectableCore::OpenRemoteClientSocket( THandle * Handle ) addr_len = sizeof( address ); if ((ClientFD = accept( Handle->FD, (struct sockaddr *)&address, &addr_len)) == -1) { - if (errno == EWOULDBLOCK) - printf( "Remote Socket: %s [*] -> Failed to accept blocking connection (%s)\n", (*RemoteClient)->Address, strerror(errno) ); + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + printf( "Remote Socket: %s [*] -> Failed to accept blocking connection (%s)\n", Handle->Address, strerror(errno) ); else - printf( "Remote Socket: %s [*] -> Failed to accept connection (%s)\n", (*RemoteClient)->Address, strerror(errno) ); - close( ClientFD ); + printf( "Remote Socket: %s [*] -> Failed to accept connection (%s)\n", Handle->Address, strerror(errno) ); return -1; } + // Set non-blocking flag + int flags = fcntl( ClientFD, F_GETFL, 0 ); + fcntl( ClientFD, F_SETFL, flags | O_NONBLOCK ); + // Return client address strcpy( ClientAddress, inet_ntoa(address.sin_addr) ); @@ -418,31 +425,30 @@ int CSelectableCore::OpenRemoteClientSocket( THandle * Handle ) } // Create Remote Client Handle - *RemoteClient = CreateHandle( "Client" ); - SetSocketHandle( "Client", ctRemoteClient, ClientAddress, 0, Handle->KeepAlive ); - SetBuffers( "Client", 20, 0, 50, "\n", 1 ); - (*RemoteClient)->FD = ClientFD; + sprintf( ClientName, "%s-%d", Handle->Name, ClientFD ); + *RemoteClient = CreateHandle( ClientName ); + SetSocketHandle( ClientName, ctRemoteClient, ClientAddress, 0, Handle->KeepAlive ); + SetBuffers( ClientName, 20, 0, 50, "\n", 1 ); + + (*RemoteClient)->FD = ClientFD; (*RemoteClient)->Parent = Handle; - (*RemoteClient)->State = csOpen; + (*RemoteClient)->State = csWaitingtoOpen; printf( "Remote Socket: %s -> Server accepted connection from client (%s)\n", Handle->Address, ClientAddress ); return (*RemoteClient)->FD; } else if (Handle->Type == ctRemoteClient) { - if (Handle->State == csWaitingtoOpen) - { - // Clear non blocking flag - int flags = fcntl( Handle->FD, F_GETFL, 0 ); - fcntl( Handle->FD, F_SETFL, (!O_NONBLOCK)&flags ); - - // Set new state - Handle->State = csOpen; - - // Log event - printf( "Socket: %s -> Client now connected to server (%s)\n", Handle->Address, Handle->Parent->Address ); + // Check state + if (Handle->State == csOpen) { + // Already open + return Handle->FD; + } + else if (Handle->State == csWaitingtoOpen) { + // Update state + Handle->State = csOpen; + return Handle->FD; } - return Handle->FD; } return -1; } @@ -459,6 +465,17 @@ int CSelectableCore::OpenClientSocket( THandle * Handle ) int TCPcnt_opt = 3; int TCPint_opt = 2; + // Check state + if (Handle->State == csOpen) { + // Already open + return Handle->FD; + } + else if (Handle->State == csWaitingtoOpen) { + // Update state + Handle->State = csOpen; + return Handle->FD; + } + // Create File descriptor if ((Handle->FD = socket( AF_INET, SOCK_STREAM, 0 )) < 0) { @@ -493,8 +510,10 @@ int CSelectableCore::OpenClientSocket( THandle * Handle ) Handle->State = csOpen; return Handle->FD; } - else if (errno == EINPROGRESS) + else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { + printf( "Client Socket: %s -> Client waiting to connect (%s)\n", Handle->Address, strerror(errno) ); + // Set status Handle->State = csWaitingtoOpen; return Handle->FD; @@ -515,6 +534,7 @@ int CSelectableCore::OpenClientSocket( THandle * Handle ) int CSelectableCore::Open( const char * HandleName ) { + int FD = -1; THandle * Handle = NULL; // Validate @@ -525,16 +545,26 @@ int CSelectableCore::Open( const char * HandleName ) // Open correctly switch (Handle->Type) { case ctPort : - return OpenPort( Handle ); + FD = OpenPort( Handle ); + break; case ctServer : - return OpenServerSocket( Handle ); + FD = OpenServerSocket( Handle ); + break; case ctClient : - return OpenClientSocket( Handle ); + FD = OpenClientSocket( Handle ); + break; case ctRemoteClient : - return OpenRemoteClientSocket( Handle ); + FD = OpenRemoteClientSocket( Handle ); + break; default: - return -1; + FD = -1; } + + // Add to Select Lists + if (Select && FD != -1) { + Select->Add( FD, true, true, this ); + } + return FD; }; //--------------------------------------------------------------------------- @@ -551,9 +581,10 @@ bool CSelectableCore::Close( THandle * Handle, bool CloseChildren ) while (ChildHandle) { if (ChildHandle->Parent == Handle) { NextHandle = ChildHandle->Next; - // Close and Destroy handle + + // Close Close( ChildHandle ); - RemoveHandle( ChildHandle ); + // Next Handle ChildHandle = NextHandle; } else { @@ -563,6 +594,11 @@ bool CSelectableCore::Close( THandle * Handle, bool CloseChildren ) } } + // Remove from Select List + if (Select) { + Select->Remove( Handle->FD, true, true ); + } + // Close Handle Fail = (close( Handle->FD ))? true : false; Handle->State = ((Fail)? csFailed : csClosed); @@ -600,28 +636,48 @@ bool CSelectableCore::Close( THandle * Handle, bool CloseChildren ) // Device Interface bool CSelectableCore::Read( THandle * Handle ) { + int ClientFD = -1; int BytesRead = 0; - int BytesWaiting = 0; + int BytesWaiting = -1; // Validate if (!Handle) { return false; } - // Check for closing event on Socket - if ((Handle->Type == ctRemoteClient) || (Handle->Type == ctClient)) + // Check for closing/opening event on Socket + if (Handle->Type == ctServer) + { + // Incoming client request + ClientFD = OpenRemoteClientSocket( Handle ); + if (ClientFD != -1) { + // Add to Select Lists + if (Select) { + Select->Add( ClientFD, true, true, this ); + } + return true; + } + } + else if ((Handle->Type == ctRemoteClient) || (Handle->Type == ctClient)) { // Check if anything to read ioctl( Handle->FD, FIONREAD, &BytesWaiting ); - if (!BytesWaiting) { - // EOF from server (close connection) + // EOF from server + if (!BytesWaiting) + { + // Close Handle Close( Handle ); - RemoveHandle( Handle ); + + // Destroy Remote Client + if (Handle->Type == ctRemoteClient) { + RemoveHandle( Handle ); + } return false; } - // Check if socket ready (non-block open not in progress) - else if (Handle->State == csWaitingtoOpen) { + // Check if socket ready (non-block open in progress) + else if (Handle->State == csWaitingtoOpen) + { printf( "Socket: %s -> Cannot read from socket in waiting\n", Name ); return false; } @@ -633,8 +689,9 @@ bool CSelectableCore::Read( THandle * Handle ) } // Read File directly into buffer - if (!(BytesRead = Handle->InBuffer->ReadFromFD( Handle->FD ))) + if (!(BytesRead = Handle->InBuffer->ReadFromFD( Handle->FD ))) { return false; + } // Process Buffer ProcessBuffer( Handle, false ); @@ -648,12 +705,59 @@ bool CSelectableCore::Read( THandle * Handle ) bool CSelectableCore::Write( THandle * Handle ) { + int BytesWritten = 0; + // Validate - if (!Handle || (Handle->State != csOpen)) { + if (!Handle) { return false; } - return true; + if (Handle->State == csWaitingtoOpen) + { + // Complete socket open + if (Handle->Type == ctRemoteClient) { + OpenRemoteClientSocket( Handle ); + } else if (Handle->Type == ctClient) { + OpenClientSocket( Handle ); + } + + // Remove from set for select write + if (Select) { + Select->Remove( Handle->FD, false, true ); + } + return true; + } + else if (Handle->State == csOpen) + { + if (Handle->OutBuffer) + { + // Write to FD directly from buffer + if (!(BytesWritten = Handle->OutBuffer->WriteToFD( Handle->FD ))) { + return false; + } + + // Update Buffer + Handle->OutBuffer->Clear( BytesWritten ); + + // Check if Buffer emtpy + if (Handle->OutBuffer->Len()) { + // Add to Select Write list + if (Select) { + Select->Remove( Handle->FD, false, true ); + } + } + } + else + { + // No Output buffer, so remove from Write list + if (Select) { + Select->Remove( Handle->FD, false, true ); + } + } + return true; + } + + return false; } //--------------------------------------------------------------------------- @@ -685,6 +789,35 @@ bool CSelectableCore::Process() } //--------------------------------------------------------------------------- +int CSelectableCore::ReadFromFD( int FD, char * Data, int MaxLen ) +{ + int BytesRead = 0; + int TotalRead = 0; + int DataRemain = 0; + + // Check if buffer created + if ((FD == -1) || !Data) { + return 0; + } + + // Read Data into buffer + DataRemain = (MaxLen == -1)? strlen(Data) : MaxLen; + while (DataRemain) + { + // Read from file descriptor + BytesRead = read( FD, Data, DataRemain ); + if (BytesRead <= 0) + break; + + // Update Data Pointers + TotalRead += BytesRead; + DataRemain -= BytesRead; + } + + return TotalRead; +} +//--------------------------------------------------------------------------- + int CSelectableCore::WriteToFD( int FD, const char * Data, int Len ) { int BytesWritten = 0; @@ -720,7 +853,7 @@ int CSelectableCore::Input( int FD, const char * Data, int Len ) int BytesWritten = 0; // Get File handle - if ((Handle = GetHandle( FD ))) { + if ((Handle = GetHandle( "Serial Port" )) || (Handle = GetHandle( "Client" ))) { // Write to handle BytesWritten = WriteToFD( Handle->FD, Data, Len ); } diff --git a/SelectableCore.h b/SelectableCore.h index 6a84a2c..9e8be0d 100644 --- a/SelectableCore.h +++ b/SelectableCore.h @@ -38,16 +38,32 @@ const char ConnectStateName[][15] = { "None", "WaitingToOpen", "Open", "DataWait //--------------------------------------------------------------------------- -void SelectConfig( long Timeout ); -void SelectClear(); -void SelectAdd( int FD, bool Read, bool Write ); -void SelectRemove( int FD, bool Read, bool Write ); -bool SelectTest(); -bool SelectCheck( int FD, bool &Read, bool &Write ); +// Previews +typedef struct SSelectHandle TSelectHandle; +typedef struct SHandle THandle; + +class CSelect; +class CSelectableCore; //--------------------------------------------------------------------------- -typedef struct SHandle THandle; +// List of Handles for Select Object +struct SSelectHandle { + // File Descriptor + int FD; + bool Read; + bool Write; + + // Event Object + CSelectableCore * Function; + + // List + TSelectHandle * Next; +}; + +//--------------------------------------------------------------------------- + +// List or Handles for Selectable Function Object struct SHandle { // Description char * Name; @@ -74,18 +90,56 @@ struct SHandle { timeval InStart; long InTimeout; // millisecs + // List / Tree THandle * Parent; THandle * Next; }; //--------------------------------------------------------------------------- +class CSelect +{ +protected: + // List + TSelectHandle * FirstHandle; + + // Select Variables + fd_set ReadTestFDS; + fd_set WriteTestFDS; + + fd_set ReadFDS; + fd_set WriteFDS; + + // Configuration + int MaxFD; + timeval Timeout; + +public: + // Life Cycle + CSelect( long SelectTimeout ); + ~CSelect(); + + // Manage FDs + void Clear(); + void Add( int FD, bool Read, bool Write, CSelectableCore * Function = NULL); + void Remove( int FD, bool Read, bool Write ); + + // Testing FDs + bool Test(); + bool Check( int FD, bool &Read, bool &Write ); +}; + +//--------------------------------------------------------------------------- + class CSelectableCore : public CFunctionCore { protected: - // Device Interfaces + // FDs THandle * FirstHandle; + // Select interface + CSelect * Select; + // Managing File Handles bool RemoveHandle( THandle * Handle ); bool DestroyHandle( THandle * Handle ); @@ -113,7 +167,7 @@ protected: // Socket Operations virtual int OpenServerSocket( THandle * Handle ); - //virtual int OpenRemoteClientSocket( THandle * Handle ); + virtual int OpenRemoteClientSocket( THandle * Handle ); virtual int OpenClientSocket( THandle * Handle ); // Mutual Operations @@ -121,6 +175,7 @@ protected: virtual bool Read( THandle * Handle ); virtual bool Write( THandle * Handle ); + int ReadFromFD( int FD, char * Data, int MaxLen ); int WriteToFD( int FD, const char * Data, int Len ); // Buffer operations @@ -128,7 +183,7 @@ protected: public: // Life Cycle - CSelectableCore( const char * Name ); + CSelectableCore( const char * Name, CSelect * Selector ); ~CSelectableCore(); // Configuration @@ -142,7 +197,6 @@ public: bool ClearHandle( const char * HandleName ); virtual int Open( const char * HandleName ); - virtual int OpenRemoteClientSocket( THandle * Handle ); virtual bool Close( const char * HandleName, bool CloseChildren = false ) { return (Close( GetHandle( HandleName ), CloseChildren )); }; virtual bool Close( int FD, bool CloseChildren = false ) { return (Close( GetHandle( FD ), CloseChildren )); };