Files
redAcore/SocketCore.cpp
Charl Wentzel 0d1c46ac53 Major update:
- Converted CSocketCore to Class/Object
- Implemented CFunctionCore as base class for CPortCore and CSocketCore
- Moved common functions to CFunctionCore and renamed
  eg. Maintain() -> Process()
- Pass Objects directly to each other as OutputFunction
- New Input() method allows data transfer between objects
2016-05-19 15:10:55 +02:00

380 lines
11 KiB
C++

/*
* SocketCore.h
*
* Created on: 13 May 2016
* Author: wentzelc
*/
// redA Libraries
#include "TimingCore.h"
#include "SocketCore.h"
#include "LogCore.h"
// Standard C/C++ Libraries
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
//---------------------------------------------------------------------------
CSocketCore::CSocketCore( const char * ServerName ) :
CFunctionCore( ServerName )
{
ServerAddress[0] = 0;
PortNo = 0;
ClientAddress[0] = 0;
ServerHandle = -1;
ServerState = ssNone;
ClientHandle = -1;
ClientState = ssNone;
}
//---------------------------------------------------------------------------
CSocketCore::~CSocketCore()
{
}
//---------------------------------------------------------------------------
int CSocketCore::OpenTCPserverSocket( const char *pAddress, int pPortNo, bool KeepAlive )
{
socklen_t addr_len;
struct sockaddr_in address;
strcpy( ServerAddress, pAddress );
PortNo = pPortNo;
// Socket options
struct linger ServerLinger_opt;
ServerLinger_opt.l_onoff = 1;
ServerLinger_opt.l_linger = 5;
int Reuse_opt = 1;
int KeepAlive_opt = 1;
int TCPidle_opt = 60;
int TCPint_opt = 15;
int TCPcnt_opt = 3;
// Create address
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(ServerAddress);
address.sin_port = htons(PortNo);
addr_len = sizeof(address);
// Set default socket state
ServerState = ssNone;
// Create socket
if ((ServerHandle = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf( "Server Socket: %s [%d] -> Failed to create server socket (%s)\n", Name, PortNo, strerror(errno) );
ServerState = ssFailed;
return -1;
};
// Configure connection
if ((setsockopt( ServerHandle, SOL_SOCKET, SO_LINGER, &ServerLinger_opt, sizeof(ServerLinger_opt)) == -1) ||
(setsockopt( ServerHandle, SOL_SOCKET, SO_REUSEADDR, &Reuse_opt, sizeof(Reuse_opt)) == -1))
{
printf( "Server Socket: %s [%d] -> Could not set socket options (%s)\n", Name, PortNo, strerror(errno) );
ServerState = ssFailed;
return -1;
}
// Configure TCP keep alive settings
if (KeepAlive &&
((setsockopt( ServerHandle, SOL_SOCKET, SO_KEEPALIVE, &KeepAlive_opt, sizeof(KeepAlive_opt)) == -1) ||
(setsockopt( ServerHandle, SOL_TCP, TCP_KEEPIDLE, &TCPidle_opt, sizeof(TCPidle_opt)) == -1) ||
(setsockopt( ServerHandle, SOL_TCP, TCP_KEEPCNT, &TCPcnt_opt, sizeof(TCPcnt_opt)) == -1) ||
(setsockopt( ServerHandle, SOL_TCP, TCP_KEEPINTVL, &TCPint_opt, sizeof(TCPint_opt)) == -1) ))
{
printf( "Server Socket: %s [%d] -> Could not set socket keepalive options (%s)\n", Name, PortNo, strerror(errno) );
ServerState = ssFailed;
return -1;
}
// Bind socket
if (bind( ServerHandle, (struct sockaddr *)&address, addr_len ) < 0)
{
printf( "Server Socket: %s [%d] -> Failed to bind server to socket (%s)\n", Name, PortNo, strerror(errno) );
close( ServerHandle );
ServerState = ssFailed;
return -1;
};
// Create que for 5 connections
if (listen( ServerHandle, 5 ) < 0)
{
printf( "Server Socket: %s [%d] -> Failed to create server socket listen que (%s)\n", Name, PortNo, strerror(errno) );
close( ServerHandle );
ServerState = ssFailed;
return -1;
};
// Server open
ServerState = ssOpen;
printf( "Server Socket: %s [%d] -> Socket binded and listening\n", Name, PortNo );
return ServerHandle;
}
//---------------------------------------------------------------------------
int CSocketCore::OpenTCPinClientSocket()
{
socklen_t addr_len;
struct sockaddr_in address;
// Set default socket state
if (ClientHandle == -1)
{
// Accept connection on current socket
addr_len = sizeof( address );
if ((ClientHandle = accept( ServerHandle, (struct sockaddr *)&address, &addr_len)) == -1)
{
if (errno == EWOULDBLOCK)
printf( "Remote Socket: %s [*] -> Failed to accept blocking connection (%s)\n", Name, strerror(errno) );
else
printf( "Remote Socket: %s [*] -> Failed to accept connection (%s)\n", Name, strerror(errno) );
close( ClientHandle );
return -1;
}
// Return client address
strcpy( ClientAddress, inet_ntoa(address.sin_addr) );
ClientState = ssOpen;
printf( "Remote Socket: %s -> Server accepted connection from client (%s)\n", Name, ClientAddress );
}
else if (ClientState == ssWaitingtoOpen)
{
// Clear non blocking flag
int flags = fcntl( ClientHandle, F_GETFL, 0 );
fcntl( ClientHandle, F_SETFL, (!O_NONBLOCK)&flags );
// Log event
printf( "Socket: %s -> Client now connected to server (%s)\n", Name, ServerAddress );
// Trigger handler & set new state
ClientState = ssOpen;
}
return ClientHandle;
}
//---------------------------------------------------------------------------
int CSocketCore::OpenTCPoutClientSocket( const char *pAddress, int pPortNo, bool KeepAlive )
{
socklen_t addr_len;
struct sockaddr_in address;
strcpy( ServerAddress, pAddress );
PortNo = pPortNo;
// Socket options
int KeepAlive_opt = 1;
int TCPidle_opt = 5;
int TCPcnt_opt = 3;
int TCPint_opt = 2;
// Set default socket state
ClientState = ssNone;
// Create File descriptor
if ((ClientHandle = socket( AF_INET, SOCK_STREAM, 0 )) < 0)
{
printf( "Client Socket: %s [*] -> Failed to create Client Socket (%s)\n", Name, strerror(errno) );
return -1;
};
// Set Non blocking open
int flags = fcntl( ClientHandle, F_GETFL, 0 );
fcntl( ClientHandle, F_SETFL, O_NONBLOCK|flags );
// Configure TCP keep alive settings
if (KeepAlive &&
((setsockopt( ClientHandle, SOL_SOCKET, SO_KEEPALIVE, &KeepAlive_opt, sizeof(KeepAlive_opt)) == -1) ||
(setsockopt( ClientHandle, SOL_TCP, TCP_KEEPIDLE, &TCPidle_opt, sizeof(TCPidle_opt)) == -1) ||
(setsockopt( ClientHandle, SOL_TCP, TCP_KEEPCNT, &TCPcnt_opt, sizeof(TCPcnt_opt)) == -1) ||
(setsockopt( ClientHandle, SOL_TCP, TCP_KEEPINTVL, &TCPint_opt, sizeof(TCPint_opt)) == -1) ))
{
printf( "Client Socket: %s -> Could not set socket keepalive options (%s)\n", Name, strerror(errno) );
return -1;
}
// Declare address
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr( ServerAddress );
address.sin_port = htons(PortNo);
addr_len = sizeof(address);
if (!connect( ClientHandle, (struct sockaddr *)&address, addr_len ))
{
// Set status
ClientState = ssOpen;
return ClientHandle;
}
else if (errno == EINPROGRESS)
{
// Set status
ClientState = ssWaitingtoOpen;
return ClientHandle;
}
else
{
printf( "Client Socket: %s -> Client failed to connect (%s)\n", Name, strerror(errno) );
// Set status
ClientState = ssFailed;
// Close socket
close( ClientHandle );
return -1;
}
}
//---------------------------------------------------------------------------
// Delete socket
bool CSocketCore::CloseTCPSocket( ESockConnectType SocketType )
{
bool Fail;
if (SocketType == ctServer) {
Fail = (close( ServerHandle ))? true : false;
ServerState = ((Fail)? ssFailed : ssClosed);
}
else {
Fail = (close( ClientHandle ))? true : false;
ClientState = ((Fail)? ssFailed : ssClosed);
}
// Show action
switch (SocketType)
{
case ctServer:
printf( "Server Socket: %s -> Server %s\n", Name, ((Fail)? "failed" : "closed") );
break;
case ctRemoteClient:
printf( "Remote Client: %s -> Connection to client %s\n", Name, ((Fail)? "failed" : "closed") );
break;
case ctClient:
printf( "Client Socket: %s -> Connection to server %s\n", Name, ((Fail)? "failed" : "closed") );
break;
case ctNone:
default:
printf( "Socket : %s -> Cannot %s socket (invalid socket type)\n", Name, ((Fail)? "fail" : "close") );
break;
};
return true;
}
//---------------------------------------------------------------------------
int CSocketCore::Input( int InputID, char * Data, int Len )
{
return WriteToFD( ClientHandle, Data, Len );
}
//---------------------------------------------------------------------------
bool CSocketCore::Read( int FD )
{
int SockBytesRead = 0;
int SockBytesWaiting = 0;
// Check if anything to read
ioctl( FD, FIONREAD, &SockBytesWaiting );
// Test for close event
if (!SockBytesWaiting)
{
// EOF from server (close connection)
CloseTCPSocket( ctRemoteClient );
ClientHandle = -1;
return false;
}
// Check if socket ready (non-block open not in progress)
else if (ClientState == ssWaitingtoOpen)
{
printf( "Socket: %s -> Cannot read from socket in waiting\n", Name );
SockBytesWaiting = 0;
}
// Handle incoming data
else
{
// Read data from socket
if (!(SockBytesRead = Buffer->ReadFromFD( FD, SockBytesWaiting ))) {
return false;
}
// Process Buffer
ProcessBuffer( false );
// Start timer
SetStartTime( &InStart );
}
return true;
}
//---------------------------------------------------------------------------
bool CSocketCore::ProcessBuffer( bool Force )
{
int Pos = 0;
int Len = 0;
char * Data = NULL;
// Check if buffered data
if (!Buffer->Len()) {
return false;
}
// Check if forced processed
if (Force)
{
// Show Packet
Len = Buffer->Peek( &Data );
ShowOutput( "Port In", OUT_NORMAL, Data, Len );
// Write buffer to Port
if (OutFunction)
OutFunction->Input( 0, Data, Len );
// Clear processed bytes from buffer
Buffer->Clear( Len );
}
else
{
// Search for end of packet marker
while (Buffer->FindChar( '\n', Pos ))
{
// Show Packet
Len = Buffer->Peek( &Data, 0, Pos+1 );
ShowOutput( "Port In", OUT_NORMAL, Data, Len );
// Write buffer to Port
if (OutFunction)
OutFunction->Input( 0, Data, Len );
// Clear processed bytes from buffer
Buffer->Clear( Len );
}
}
return true;
}
//---------------------------------------------------------------------------