- Convert PortCore functions into Class Bug fixes: - Correctly handle socket close event (return false on read) - Only write to port/socket if valid file handle
377 lines
12 KiB
C++
377 lines
12 KiB
C++
/*
|
|
* SocketCore.h
|
|
*
|
|
* Created on: 13 May 2016
|
|
* Author: wentzelc
|
|
*/
|
|
|
|
// redA Libraries
|
|
#include "TimingCore.h"
|
|
#include "SocketCore.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>
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Variables
|
|
char * ServerName = NULL;
|
|
char ServerAddress[25] = "";
|
|
int PortNo = 0;
|
|
char ClientAddress[25] = "";
|
|
|
|
int ServerHandle = -1;
|
|
ESocketState ServerState = ssNone;
|
|
|
|
int ClientHandle = -1;
|
|
ESocketState ClientState = ssNone;
|
|
|
|
char SockInBuffer[500] = "";
|
|
int SockInBufLen = 500;
|
|
int SockInLen = 0;
|
|
|
|
// SockIn Timer
|
|
timeval SockInStart = { 0, 0 };
|
|
long SockInTimeout = 100; // millisecs
|
|
|
|
int SockBytesRead = 0;
|
|
int SockBytesWaiting = 0;
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int OpenTCPserverSocket( const char *pName, const char *pAddress, int pPortNo, bool KeepAlive )
|
|
{
|
|
socklen_t addr_len;
|
|
struct sockaddr_in address;
|
|
|
|
ServerName = (char*)malloc( strlen(pName)+1 );
|
|
strcpy( ServerName, pName );
|
|
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", ServerName, 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", ServerName, 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", ServerName, 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", ServerName, 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", ServerName, PortNo, strerror(errno) );
|
|
close( ServerHandle );
|
|
ServerState = ssFailed;
|
|
return -1;
|
|
};
|
|
|
|
// Server open
|
|
ServerState = ssOpen;
|
|
printf( "Server Socket: %s [%d] -> Socket binded and listening\n", ServerName, PortNo );
|
|
return ServerHandle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int 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", ServerName, strerror(errno) );
|
|
else
|
|
printf( "Remote Socket: %s [*] -> Failed to accept connection (%s)\n", ServerName, 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", ServerName, 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", ServerName, ServerAddress );
|
|
|
|
// Trigger handler & set new state
|
|
ClientState = ssOpen;
|
|
}
|
|
|
|
return ClientHandle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int OpenTCPoutClientSocket( const char *pName, const char *pAddress, int pPortNo, bool KeepAlive )
|
|
{
|
|
socklen_t addr_len;
|
|
struct sockaddr_in address;
|
|
|
|
ServerName = (char*)malloc( strlen(pName)+1 );
|
|
strcpy( ServerName, pName );
|
|
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", ServerName, 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", ServerName, 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", ServerName, strerror(errno) );
|
|
|
|
// Set status
|
|
ClientState = ssFailed;
|
|
|
|
// Close socket
|
|
close( ClientHandle );
|
|
return -1;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Delete socket
|
|
bool 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", ServerName, ((Fail)? "failed" : "closed") );
|
|
break;
|
|
|
|
case ctRemoteClient:
|
|
printf( "Remote Client: %s -> Connection to client %s\n", ServerName, ((Fail)? "failed" : "closed") );
|
|
break;
|
|
|
|
case ctClient:
|
|
printf( "Client Socket: %s -> Connection to server %s\n", ServerName, ((Fail)? "failed" : "closed") );
|
|
break;
|
|
|
|
case ctNone:
|
|
default:
|
|
printf( "Socket : %s -> Cannot %s socket (invalid socket type)\n", ServerName, ((Fail)? "fail" : "close") );
|
|
break;
|
|
};
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool ReadClientSocket()
|
|
{
|
|
// Check if anything to read
|
|
ioctl( ClientHandle, 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", ServerName );
|
|
SockBytesWaiting = 0;
|
|
}
|
|
// Handel incoming data
|
|
else
|
|
{
|
|
// Read data into buffer
|
|
SockBytesRead = read( ClientHandle, &SockInBuffer[SockInLen], SockBytesWaiting ); // SockInBufLen-SockInLen
|
|
|
|
// Check of errors
|
|
if (SockBytesRead == -1)
|
|
{
|
|
printf( "Socket: %s -> Cannot read from socket (%s)\n", ServerName, strerror(errno) );
|
|
SockBytesRead = 0;
|
|
|
|
// Check of non-blocking write
|
|
// bool Blocked = ((errno == EAGAIN)? true : false );
|
|
}
|
|
else if (SockBytesRead > 0)
|
|
{
|
|
// Process Reply
|
|
SockInLen += SockBytesRead;
|
|
|
|
// Reset timer
|
|
SetStartTime( &SockInStart );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool MaintainSocket( int PortHandle )
|
|
{
|
|
// Init vars
|
|
long Duration = 0;
|
|
int BytesWritten = 0;
|
|
int StartWrite = 0;
|
|
|
|
if (SockInLen > 0)
|
|
{
|
|
// Check duration since last PortIn
|
|
Duration = TimePassed( SockInStart );
|
|
if (Duration > SockInTimeout)
|
|
{
|
|
// Handle buffer as packet
|
|
SockInBuffer[ SockInLen ] = 0;
|
|
//ShowOutput( "Sock", SockInBuffer, SockInLen, Duration );
|
|
|
|
// Write output to Port
|
|
BytesWritten = 0;
|
|
StartWrite = 0;
|
|
if (PortHandle != -1) {
|
|
while (StartWrite < SockInLen) {
|
|
BytesWritten = write( PortHandle, &SockInBuffer[StartWrite], SockInLen );
|
|
StartWrite += BytesWritten;
|
|
}
|
|
}
|
|
|
|
// Reset buffer and timer
|
|
SockInLen = 0;
|
|
SetInterval( &SockInStart, 0 );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|