diff --git a/BaseCrossSocket.cpp b/BaseCrossSocket.cpp new file mode 100644 index 0000000..fa16a30 --- /dev/null +++ b/BaseCrossSocket.cpp @@ -0,0 +1,770 @@ +// CrossSockets +// Copyright Jeroen Saey +// Created 27-01-2013 +// BaseCrossSocket.cpp + +#include "BaseCrossSocket.h" +#include "CrossSocketPlatform.h" +#include "CrossSocket.h" +#include "CrossThreads.h" +#include "CrossThreadsHandler.h" + +#include +#include +#include +#include +#include +#include + +// Create the constructor +BaseCrossSocket::BaseCrossSocket() +{ + _socketHandle = -1; + _closeSignal = false; + + // Initialize default values + _error = ""; + _blockingMode = nonblocking; + _timeoutSeconds = 0; + _timeoutMicroSeconds = 0; + _password = ""; + _threadHandler = new CrossThreadsHandler(); + + #ifdef __WIN32__ + static bool firstTime = true; + if (firstTime) + { + WSAData wsaData; + + if ((int i = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) + { + handleError(NULL, "BaseCrossSocket - WSAStartup() failed: "); + exit(-1); + } + + // cleanup + atexit(WSA_exit); + firstTime = false; + } + #endif +} + +BaseCrossSocket::~BaseCrossSocket() +{ + if (_socketHandle > 0) + { + close(_socketHandle); + } +} + +bool BaseCrossSocket::listen(int connectionLimit, CrossSocketErrors *error) +{ + fprintf(stderr,"This application is using: CrossSockets\n" + " Created by Jeroen Saey\n" + "========================================\n"); + + getSocket(); + + char alreadyInUse = 1; + setsockopt(_socketHandle, SOL_SOCKET, SO_REUSEADDR, &alreadyInUse, sizeof(int)); + + if(::listen(_socketHandle, connectionLimit) == -1) + { + handleError(error, "BaseCrossSocket::listen() error: "); + return false; + } + + noError(error); + return true; +} + +BaseCrossSocket* BaseCrossSocket::accept(CrossSocketErrors *error) +{ + int remoteSocketHandle = -1; + sockaddr remoteAddress; + + if (!waitRead(error)) + { + return NULL; + } + + sw_socklen_t socketSize = sizeof(sockaddr); + + if ((remoteSocketHandle = ::accept(_socketHandle, &remoteAddress, &socketSize)) == int(SOCKETFAIL)) + { + handleError(error, "BaseCrossSocket::accept() error: "); + return NULL; + } + + // Check if we are in non blocking mode + if (_blockingMode == nonblocking) + { + fcntl(remoteSocketHandle, F_SETFL, O_NONBLOCK); + } + + // Create a new class of the BaseCrossSocket + BaseCrossSocket* clientSocket = create(remoteSocketHandle, error); + + noError(error); + + stringstream clientInformation; + clientInformation << ((CrossSocket*)clientSocket)->getClientAddress() << " is connected!\n" + << ((CrossSocket*)clientSocket)->getClientAddress() << " : " << ((CrossSocket*)clientSocket)->getClientPort() << "\n"; + + cout << clientInformation.str(); + + return clientSocket; +} + +bool BaseCrossSocket::checkPassword(BaseCrossSocket *socket) +{ + if (_password.length() > 0) + { + socket->writeLine("This server is protected with a password, please insert the password now."); + + int passwordCounter = 0; + string givenPassword; + while (givenPassword != _password) + { + // Just read 50 bytes for the password + givenPassword = socket->readLine(50); + if (givenPassword != _password) + { + socket->writeLine("Incorrect password given."); + passwordCounter++; + if (passwordCounter >= 5) + { + socket->writeLine("Too many attempts where made to enter the correct password."); + socket->writeLine("Have a nice day!"); + socket->writeLine("Disconnecting..."); + socket->writeLine("403"); + disconnect(); + return false; + } + } + } + } + socket->writeLine("The password is correct. Access granted"); + return true; +} + +bool BaseCrossSocket::isConnected() +{ + return _socketHandle > 0; +} + +bool BaseCrossSocket::disconnect(CrossSocketErrors *error) +{ + int i = 0; + char buffer[256]; + + if (_socketHandle < 0) + { + setError(error, notConnected, "BaseCrossSocket::disconnect() - No connection"); + return false; + } + + // check if the client closed the connection + if (shutdown(_socketHandle, 1) != 0) + { + handleError(error, "BaseCrossSocket::disconnect() error: "); + return false; + } + + CrossSocketErrors crossSocketError; + + // Check if we got a signal from the client that he closed the connection + if (!_closeSignal) + { + while (true) + { + if (!waitRead(error)) + { + return false; + } + + i = readLine(buffer, 256, &crossSocketError); + + if (i <= 0) + { + break; + } + + // If we do not want to wait we need to throw an error + if (_blockingMode == noWait) + { + setError(error, notReady, "BaseCrossSocket::disconnect() - We need more time, please call again"); + return false; + } + } + } + + if (i != 0) + { + setError(error, crossSocketError, _error); + return false; + } + + // Reset the state + reset(); + + close(_socketHandle); + _socketHandle = -1; + + noError(error); + cout << "CrossSockets: Client has been disconnected!\n"; + return true; +} + +bool BaseCrossSocket::closeSocketHandle() +{ + if (_socketHandle > 0) + { + close(_socketHandle); + _socketHandle = -1; + + //reset state + reset(); + + return true; + } + return false; +} + +int BaseCrossSocket::writeLine(string message, CrossSocketErrors *error) +{ + //TODO: Why cant we append the data? + //int returnValue = send(_socketHandle, message.append("\r\n").data(), strlen(message.data()), 0); + int returnValue = send(_socketHandle, message.data(), message.size(), 0); + send(_socketHandle, "\r\n", 2, 0); + return returnValue; +} + +int BaseCrossSocket::writeLine(string message, size_t length, CrossSocketErrors *error) +{ + int returnValue = send(_socketHandle, message.data(), length, 0); + send(_socketHandle, "\r\n", 2, 0); + return returnValue; +} + +int BaseCrossSocket::write(const string message, size_t length, CrossSocketErrors *error) +{ + return send(_socketHandle, message.data(), length, 0); +} + +int BaseCrossSocket::write(const string message, CrossSocketErrors *error) +{ + return send(_socketHandle, message.data(), message.size(), 0); +} + +void BaseCrossSocket::setPassword(const string password) +{ + _password = password; +} + +ssize_t BaseCrossSocket::read(char *buffer, CrossSocketErrors *error) +{ + ssize_t returnValue = 0; + char singleCharacter; + + if (_socketHandle < 0) + { + setError(error, notConnected, "BaseCrossSocket::read() - No connection"); + return -1; + } + + if (!waitRead(error)) + { + return -1; + } + + while (size_t i = recv(_socketHandle, buffer + returnValue, int(sizeof(buffer) - returnValue), 0)) + { + if (i == 0) + { + break; + } + returnValue += i; + if (returnValue > sizeof(buffer) -1) + { + break; + } + } + return returnValue; +} + +ssize_t BaseCrossSocket::read(char *buffer, int bytes, CrossSocketErrors *error) +{ + ssize_t returnValue = 0; + char singleCharacter; + + if (_socketHandle < 0) + { + setError(error, notConnected, "BaseCrossSocket::read() - No connection"); + return -1; + } + + if (!waitRead(error)) + { + return -1; + } + + while (size_t i = recv(_socketHandle, buffer + returnValue, int(bytes - returnValue), 0)) + { + if (i == 0) + { + break; + } + returnValue += i; + if (returnValue > bytes -1) + { + break; + } + } + return returnValue; +} + +string BaseCrossSocket::readLine(int size, CrossSocketErrors *error) +{ + char *buffer = new char[size]; + ssize_t returnValue = 0; + char singleCharacter; + + if (_socketHandle < 0) + { + setError(error, notConnected, "BaseCrossSocket::readLine() - No connection"); + } + + if (!waitRead(error)) + { + return string(); + } + + while (size_t i = recv(_socketHandle, &singleCharacter, 1, 0)) + { + if (returnValue >= size || singleCharacter == '\n') + { + break; + } + + if (singleCharacter != '\r') + { + buffer[returnValue++] = singleCharacter; + } + } + buffer[returnValue] = '\0'; + + if (returnValue < 0) + { + handleError(error, "BaseCrossSocket::readLine() error: "); + } + else if (returnValue == 0) + { + _closeSignal = true; //we received a close signal from client + setError(error, terminated, "BaseCrossSocket::readLine() - Connection terminated by client"); + } + else + { + noError(error); + } + return string(buffer); +} + +ssize_t BaseCrossSocket::readLine(char *buffer, int bytes, CrossSocketErrors *error) +{ + ssize_t returnValue = 0; + char singleCharacter; + + if (_socketHandle < 0) + { + setError(error, notConnected, "BaseCrossSocket::readLine() - No connection"); + return -1; + } + + if (!waitRead(error)) + { + return -1; + } + + while (size_t i = recv(_socketHandle, &singleCharacter, 1, 0)) + { + if (returnValue >= sizeof(buffer) || singleCharacter == '\n') + { + break; + } + + if (singleCharacter != '\r') + { + buffer[returnValue++] = singleCharacter; + } + } + buffer[returnValue] = '\0'; + + if (returnValue < 0) + { + handleError(error, "BaseCrossSocket::readLine() error: "); + } + else if (returnValue == 0) + { + _closeSignal = true; //we received a close signal from client + setError(error, terminated, "BaseCrossSocket::readLine() - Connection terminated by client"); + } + else + { + noError(error); + } + return returnValue; +} + +int BaseCrossSocket::getClientSocket(CrossSocketErrors *error) +{ + if (_socketHandle > 0) + { + noError(error); + return _socketHandle; + } + + setError(error, notConnected, "BaseCrossSocket::getClientSocket() - No descriptor"); + return -1; +} + +bool BaseCrossSocket::getServerHost(sockaddr *host, CrossSocketErrors *error) +{ + if (host == NULL) + { + setError(error, fatal, "BaseCrossSocket::getServerHost() - Got NULL pointer"); + return false; + } + + if (_socketHandle < 0) + { + setError(error, notConnected, "BaseCrossSocket::getServerHost() - No socket"); + return false; + } + + sw_socklen_t temp = sizeof(sockaddr); + if (getsockname(_socketHandle, host, &temp) != 0) + { + handleError(error, "BaseCrossSocket::getServerHost() error: "); + return false; + } + + noError(error); + return true; +} + +void BaseCrossSocket::setTimeout(unsigned int seconds, unsigned int miliSeconds) +{ + _timeoutSeconds = seconds; + _timeoutMicroSeconds = miliSeconds; +} + +bool BaseCrossSocket::getClientHost(sockaddr *client, CrossSocketErrors *error) +{ + if (client == NULL) + { + setError(error, fatal, "BaseCrossSocket::getClientHost() - Got NULL pointer"); + return false; + } + + if (_socketHandle > 0) + { + sw_socklen_t temp = sizeof(sockaddr); + + if (getpeername(_socketHandle, client, &temp) != 0) + { + handleError(error, "BaseCrossSocket::getClientHost() error: "); + return false; + } + } + else + { + setError(error, notConnected, "BaseCrossSocket::getClientHost() - No connection"); + return false; + } + + noError(error); + return true; +} + +void BaseCrossSocket::reset() +{ + _closeSignal = false; +} + +bool BaseCrossSocket::waitIO(ioTypeEnum &type, CrossSocketErrors *error) +{ + if (_blockingMode != blocking) + { + noError(error); + return true; + } + + // Wait with select() even if no timeout is set + + timeval time; + timeval *timeForever = NULL; // Forever waiting + time.tv_sec = _timeoutSeconds; + time.tv_usec = _timeoutMicroSeconds; + + if (_timeoutSeconds > 0 || _timeoutMicroSeconds > 0) + { + timeForever = &time; + } + + fd_set readFileDescriptorSet; + fd_set writeFileDescriptorSet; + fd_set exceptionFileDescriptorSet; + + FD_ZERO (&readFileDescriptorSet); + FD_ZERO (&writeFileDescriptorSet); + FD_ZERO (&exceptionFileDescriptorSet); + FD_SET (_socketHandle, &readFileDescriptorSet); + FD_SET (_socketHandle, &writeFileDescriptorSet); + FD_SET (_socketHandle, &exceptionFileDescriptorSet); + + int returnValue = 0; + + switch (type) + { + case ioread: + returnValue = select(_socketHandle+1, &readFileDescriptorSet, NULL, NULL, timeForever); + break; + case iowrite: + returnValue = select(_socketHandle+1, NULL, &writeFileDescriptorSet, NULL, timeForever); + break; + case ioexception: + returnValue = select(_socketHandle+1, NULL, NULL, &exceptionFileDescriptorSet, timeForever); + break; + case ioreadWrite: + returnValue = select(_socketHandle+1, &readFileDescriptorSet, &writeFileDescriptorSet, NULL, timeForever); + break; + case ioall: + returnValue = select(_socketHandle+1, &readFileDescriptorSet, &writeFileDescriptorSet, &exceptionFileDescriptorSet, timeForever); + break; + } + + if (returnValue == 0) + { + setError(error, timeout, "BaseCrossSocket::waitIO() timeout"); + return false; + } + else if (returnValue < 0) + { + handleError(error, "BaseCrossSocket::waitIO() error: "); + return false; + } + + if (FD_ISSET(_socketHandle, &readFileDescriptorSet)) + { + noError(error); + type = ioread; + return true; + } + + if (FD_ISSET(_socketHandle, &writeFileDescriptorSet)) + { + noError(error); + type = iowrite; + return true; + } + + if (FD_ISSET(_socketHandle, &exceptionFileDescriptorSet)) + { + noError(error); + type = ioexception; + return true; + } + + setError(error, fatal, "BaseCrossSocket::waitIO() failed on select()"); + return false; +} + +bool BaseCrossSocket::waitRead(CrossSocketErrors *error) +{ + ioTypeEnum temp = ioread; + return waitIO(temp, error); +} + +bool BaseCrossSocket::waitWrite(CrossSocketErrors *error) +{ + ioTypeEnum temp = iowrite; + return waitIO(temp, error); +} + +string BaseCrossSocket::getError() +{ + return _error; +} + +void BaseCrossSocket::printError() +{ + if (_error.size() > 0) + { + fprintf(stderr, "CrossSocket error:\n%s!\n", _error.data()); + } +} + +void BaseCrossSocket::handleError(CrossSocketErrors *error, string message) +{ + #ifdef __WIN32__ + // Winsock2 errorList (taken from winsock2.h) + switch (WSAGetLastError()) + { + case 0: message += "No error"; break; + case WSAEINTR: message += "Interrupted system call"; break; + case WSAEBADF: message += "Bad file number"; break; + case WSAEACCES: message += "Permission denied"; break; + case WSAEFAULT: message += "Bad address"; break; + case WSAEINVAL: message += "Invalid argument"; break; + case WSAEMFILE: message += "Too many open sockets"; break; + case WSAEWOULDBLOCK: message += "Operation would block"; break; + case WSAEINPROGRESS: message += "Operation now in progress"; break; + case WSAEALREADY: message += "Operation already in progress"; break; + case WSAENOTSOCK: message += "Socket operation on non-socket"; break; + case WSAEDESTADDRREQ: message += "Destination address required"; break; + case WSAEMSGSIZE: message += "Message too long"; break; + case WSAEPROTOTYPE: message += "Protocol wrong type for socket"; break; + case WSAENOPROTOOPT: message += "Bad protocol option"; break; + case WSAEPROTONOSUPPORT: message += "Protocol not supported"; break; + case WSAESOCKTNOSUPPORT: message += "Socket type not supported"; break; + case WSAEOPNOTSUPP: message += "Operation not supported on socket"; break; + case WSAEPFNOSUPPORT: message += "Protocol family not supported"; break; + case WSAEAFNOSUPPORT: message += "Address family not supported"; break; + case WSAEADDRINUSE: message += "Address already in use"; break; + case WSAEADDRNOTAVAIL: message += "Can't assign requested address"; break; + case WSAENETDOWN: message += "Network is down"; break; + case WSAENETUNREACH: message += "Network is unreachable"; break; + case WSAENETRESET: message += "Net connection reset"; break; + case WSAECONNABORTED: message += "Software caused connection abort"; break; + case WSAECONNRESET: message += "Connection reset by client"; break; + case WSAENOBUFS: message += "No buffer space available"; break; + case WSAEISCONN: message += "Socket is already connected"; break; + case WSAENOTCONN: message += "Socket is not connected"; break; + case WSAESHUTDOWN: message += "Can't send after socket shutdown"; break; + case WSAETOOMANYREFS: message += "Too many references"; break; + case WSAETIMEDOUT: message += "Connection timed out"; break; + case WSAECONNREFUSED: message += "Connection refused"; break; + case WSAELOOP: message += "Too many levels of symbolic links"; break; + case WSAENAMETOOLONG: message += "File name too long"; break; + case WSAEHOSTDOWN: message += "Host is down"; break; + case WSAEHOSTUNREACH: message += "No route to host"; break; + case WSAENOTEMPTY: message += "Directory not empty"; break; + case WSAEPROCLIM: message += "Too many processes"; break; + case WSAEUSERS: message += "Too many users"; break; + case WSAEDQUOT: message += "Disc quota exceeded"; break; + case WSAESTALE: message += "Stale NFS file handle"; break; + case WSAEREMOTE: message += "Too many levels of remote in path"; break; + case WSASYSNOTREADY: message += "Network system is unavailable"; break; + case WSAVERNOTSUPPORTED: message += "Winsock version out of range"; break; + case WSANOTINITIALISED: message += "WSAStartup not yet called"; break; + case WSAEDISCON: message += "Graceful shutdown in progress"; break; + case WSAHOST_NOT_FOUND: message += "Host not found"; break; + case WSANO_DATA: message += "No host data of that type was found"; break; + default: message += "Unknown Winsock error: " + WSAGetLastError(); break; + } + #else + message += strerror(errno); + #endif + + int errorNumber; + + #ifdef __WIN32__ + errorNumber = WSAGetLastError(); + #else + errorNumber = errno; + #endif + + CrossSocketErrorsEnum errorMessage; + + if (errorNumber == EADDRINUSE) + { + errorMessage = portInUse; + } + else if (errorNumber == EAGAIN || errorNumber == EWOULDBLOCK) + { + errorMessage = notReady; + } + else if (errorNumber == EMSGSIZE) + { + errorMessage = messageTooLong; + } + else if (errorNumber == EINPROGRESS || errorNumber == EALREADY) + { + errorMessage = notReady; + } + else if (errorNumber == ECONNREFUSED || errorNumber == ETIMEDOUT) + { + errorMessage = noResponse; + } + else if (errorNumber == ENOTCONN || errorNumber == EBADF || errorNumber == ENOTSOCK) + { + errorMessage = notConnected; + } + else if (errorNumber == EPIPE) + { + errorMessage = terminated; + _closeSignal = true; + } + else if (errorNumber == EINTR) + { + errorMessage = interrupted; + } + else + { + errorMessage = fatal; //default + } + + setError(error, errorMessage, message); +} + +void BaseCrossSocket::noError(CrossSocketErrors *error) +{ + if (error != NULL) + { + *error = ok; + error->_error = ""; + error->failed_class = NULL; + } +} + +void BaseCrossSocket::setError(CrossSocketErrors *error, CrossSocketErrors name, string message) +{ + _error = message; + + if (error != NULL) + { + *error = name; + error->_error = message; + error->failed_class = this; + } + else + { + if (ERRORMODE == 0) + { + printError(); + if (name == terminated) + { + disconnect(); + } + } + else if (ERRORMODE == 1) + { + // Reset the state + reset(); + + close(_socketHandle); + _socketHandle = -1; + + CrossSocketErrors crossSocketError; + crossSocketError = name; + crossSocketError._error = message; + crossSocketError.failed_class = this; + throw crossSocketError; + } + else + { + exit(-1); + } + } +} diff --git a/BaseCrossSocket.h b/BaseCrossSocket.h new file mode 100644 index 0000000..4f2e0b9 --- /dev/null +++ b/BaseCrossSocket.h @@ -0,0 +1,107 @@ +// CrossSockets +// Copyright Jeroen Saey +// Created 27-01-2013 +// BaseCrossSocket.h + +#pragma once + +#include "CrossSocketsCore.h" +#include "CrossSocketConfig.h" +#include "CrossSocketErrors.h" + +#include +#include + +class CrossThreadsHandler; +class CrossSocket; + +class BaseCrossSocket +{ +public: + BaseCrossSocket(); + virtual ~BaseCrossSocket(); + + // Blocking : Everything blocks until operation is done + // noWait : First call time blocking only + // nonBlocking : Allow everything simultaneously + enum blockTypeEnum + { + blocking, + noWait, + nonblocking + }; + + // wait for I/O (with timeout) + enum ioTypeEnum + { + ioread, + iowrite, + ioexception, + ioreadWrite, + ioall + }; + + virtual bool listen(int connectionLimit = 5, CrossSocketErrors* = NULL); + virtual BaseCrossSocket* accept(CrossSocketErrors* = NULL); + + virtual bool isConnected(); + virtual bool disconnect(CrossSocketErrors* = NULL); + virtual bool checkPassword(BaseCrossSocket *socket); + + // force to close the socket + virtual bool closeSocketHandle(); + + virtual int write(const std::string message, CrossSocketErrors *error = NULL); + virtual int write(const std::string message, size_t length, CrossSocketErrors *error = NULL); + virtual int writeLine(std::string message, CrossSocketErrors *error = NULL); + virtual int writeLine(std::string message, size_t length, CrossSocketErrors *error = NULL); + virtual ssize_t read(char *buffer, int bytes, CrossSocketErrors *error = NULL); + virtual ssize_t read(char *buffer, CrossSocketErrors *error = NULL); + virtual ssize_t readLine(char *buffer, int bytes, CrossSocketErrors *error = NULL); + virtual std::string readLine(int size, CrossSocketErrors *error = NULL); + virtual void setPassword(const std::string password); + + virtual int getClientSocket(CrossSocketErrors *error); + virtual bool getServerHost(sockaddr *host, CrossSocketErrors *error = NULL); + virtual bool getClientHost(sockaddr *client, CrossSocketErrors *error = NULL); + + // Receive timeout (can only be used in blocking mode) + void setTimeout(unsigned int seconds, unsigned int miliseconds); + + // Error handling + virtual void printError(); + virtual std::string getError(); + +protected: + virtual void getSocket() = 0; + virtual BaseCrossSocket* create(int socketdescriptor, CrossSocketErrors *error) = 0; + virtual void reset(); + + virtual bool waitIO(ioTypeEnum &type, CrossSocketErrors *error); + bool waitRead(CrossSocketErrors *error); + bool waitWrite(CrossSocketErrors *error); + + virtual void handleError(CrossSocketErrors *error, std::string message); + virtual void noError(CrossSocketErrors *error); + virtual void setError(CrossSocketErrors *error, CrossSocketErrors name, std::string message); + + int _socketHandle; + std::string _password; + + BaseCrossSocket *_clientSocket; + + // last error + std::string _error; + + // have we received a shutdown signal? + bool _closeSignal; + + // blocking mode + blockTypeEnum _blockingMode; + + CrossThreadsHandler *_threadHandler; + + // timeout for waitIO() + int _timeoutSeconds; + int _timeoutMicroSeconds; +}; diff --git a/CrossSocket.cpp b/CrossSocket.cpp new file mode 100644 index 0000000..e74f798 --- /dev/null +++ b/CrossSocket.cpp @@ -0,0 +1,252 @@ +// CrossSockets +// Copyright Jeroen Saey +// Created 27-01-2013 +// CrossSocket.cpp + +#include "CrossSocket.h" + +#ifndef __WIN32__ + #include + #include + #include + #include + #include +#else + #define F_SETFL FIONBIO + #define O_NONBLOCK 1 + + extern int close(int fd); + extern int fcntl(int fd, int cmd, long arg); +#endif + +using namespace std; + +CrossSocket::CrossSocket(blockTypeEnum block) +{ + _blockingMode = block; +} + +CrossSocket::~CrossSocket() +{ + +} + +void CrossSocket::getSocket() +{ + if (_socketHandle < 0) + { + _socketHandle = socket(PF_INET, SOCK_STREAM, 0); + + if (_blockingMode == nonblocking) + { + fcntl(_socketHandle, F_SETFL, O_NONBLOCK); + } + + //reset state + reset(); + } +} + +BaseCrossSocket* CrossSocket::create(int socketdescriptor, CrossSocketErrors *error) +{ + CrossSocket* remoteClass; + + remoteClass = new CrossSocket(_blockingMode); + remoteClass->_socketHandle = socketdescriptor; + + noError(error); + return remoteClass; +} + +bool CrossSocket::bind(int port, CrossSocketErrors *error) +{ + return bind(port, "", error); +} + +bool CrossSocket::bind(int port, string host, CrossSocketErrors *error) +{ + hostent *hostEntry; + in_addr networkAddress; + + if (host.size() > 0 ) + { + // Bind to a specific address + + if ((hostEntry = gethostbyname(host.data())) == NULL) + { + setError(error, fatal, "CrossSocket::bind() - Can't get host by name"); + return false; + } + + networkAddress = *((in_addr *)hostEntry->h_addr); + } + else + { + // Bind to any address + networkAddress.s_addr = INADDR_ANY; + } + + getSocket(); + + sockaddr_in serverAddress; + + memset(&serverAddress, 0, sizeof(serverAddress)); + serverAddress.sin_family = AF_INET; + serverAddress.sin_port = htons(port); + serverAddress.sin_addr.s_addr = networkAddress.s_addr; + + if (::bind(_socketHandle, (sockaddr *)&serverAddress, sizeof(serverAddress)) == -1) + { + handleError(error, "CrossSocket::bind() error: "); + return false; + } + + noError(error); + return true; +} + +bool CrossSocket::connect(int port, string hostname, CrossSocketErrors *error) +{ + getSocket(); + + hostent *host; + + if ((host = gethostbyname(hostname.data())) == NULL ) + { + setError(error, fatal, "CrossSocket::connect() - Can't get host by name"); + return false; + } + + sockaddr_in clientAddress; + + memset(&clientAddress, 0, sizeof(clientAddress)); + clientAddress.sin_family = AF_INET; + clientAddress.sin_port = htons(port); + clientAddress.sin_addr = *((in_addr *)host->h_addr); + + if (::connect(_socketHandle, (sockaddr *)&clientAddress, sizeof(clientAddress)) == -1) + { + handleError(error, "CrossSocket::connect() error: "); + return false; + } + + noError(error); + return true; +} + + +string CrossSocket::getClientAddress(CrossSocketErrors *error) +{ + sockaddr_in clientAddress; + + if (!getClientHost((sockaddr *)&clientAddress, error)) + { + return ""; + } + + char *addressPointer; + + if ((addressPointer = inet_ntoa(clientAddress.sin_addr)) == NULL ) + { + setError(error, fatal, "CrossSocket::getClientHostName() - Can't get client address"); + return ""; + } + + noError(error); + return string (addressPointer); +} + +int CrossSocket::getClientPort(CrossSocketErrors *error) +{ + sockaddr_in clientAddress; + + if (!getClientHost((sockaddr *)&clientAddress, error)) + { + return -1; + } + + noError(error); + + return ntohs(clientAddress.sin_port); +} + +string CrossSocket::getClientHostName(CrossSocketErrors *error) +{ + string name = getClientAddress(error); + + if (name.size() < 1) + { + return ""; + } + + hostent *host; + + if ((host = gethostbyname(name.data())) == NULL ) + { + setError(error, fatal, "CrossSocket::getClientHostName() - Can't get client by address"); + return ""; + } + + noError(error); + return string(host->h_name);; +} + +string CrossSocket::getServerAddress(CrossSocketErrors *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 = getServerHostName(error); + if (name.size() < 1) + { + return ""; + } + + hostent *host; + + if ((host = gethostbyname(name.data())) == NULL ) + { + setError(error, fatal, "CrossSocket::getServerAddress() - Can't get host by name"); + return ""; + } + + char *addressPointer; + + if ((addressPointer = inet_ntoa(*((in_addr *)host->h_addr))) == NULL) + { + setError(error, fatal, "CrossSocket::getServerAddress() - Can't get host address"); + return ""; + } + return string(addressPointer); +} + +int CrossSocket::getServerPort(CrossSocketErrors *error) +{ + sockaddr_in address; + + if (!getServerHost((sockaddr *)&address, error)) + { + return -1; + } + + noError(error); + + return ntohs(address.sin_port); +} + +string CrossSocket::getServerHostName(CrossSocketErrors *error) +{ + char buffer[256]; + + if (gethostname(buffer, 256) != 0) + { + handleError(error, "CrossSocket::gethostname() error: "); + return ""; + } + + string message(buffer); + + noError(error); + return message; +} diff --git a/CrossSocket.h b/CrossSocket.h new file mode 100644 index 0000000..3985af2 --- /dev/null +++ b/CrossSocket.h @@ -0,0 +1,34 @@ +// CrossSockets +// Copyright Jeroen Saey +// Created 27-01-2013 +// CrossSocket.h + +#pragma once + +#include "CrossSocketsCore.h" +#include "BaseCrossSocket.h" +#include + +class CrossSocket : public BaseCrossSocket +{ +public: + CrossSocket(blockTypeEnum block = blocking); + virtual ~CrossSocket(); + + virtual bool bind(int port, CrossSocketErrors *error = NULL); //use port=0 to get any free port + virtual bool bind(int port, std::string host, CrossSocketErrors *error = NULL); //you can also specify the host interface to use + virtual bool connect(int port, std::string hostname, CrossSocketErrors *error = NULL); + + // Tools + // Gets IP address, name or port. + virtual std::string getClientAddress(CrossSocketErrors *error = NULL); + virtual int getClientPort(CrossSocketErrors *error = NULL); + virtual std::string getClientHostName(CrossSocketErrors *error = NULL); + virtual std::string getServerAddress(CrossSocketErrors *error = NULL); + virtual int getServerPort(CrossSocketErrors *error = NULL); + virtual std::string getServerHostName(CrossSocketErrors *error = NULL); + +protected: + virtual void getSocket(); + virtual BaseCrossSocket* create(int socketdescriptor, CrossSocketErrors *error); +}; diff --git a/CrossSocketConfig.h b/CrossSocketConfig.h new file mode 100644 index 0000000..8e42299 --- /dev/null +++ b/CrossSocketConfig.h @@ -0,0 +1,9 @@ +// CrossSockets +// Copyright Jeroen Saey +// Created 27-01-2013 +// CrossSocketConfig.h + +#pragma once + +// Set the errorMode (0: Verbose error 1: Throw error) +#define ERRORMODE 0 diff --git a/CrossSocketErrors.cpp b/CrossSocketErrors.cpp new file mode 100644 index 0000000..270d39d --- /dev/null +++ b/CrossSocketErrors.cpp @@ -0,0 +1,52 @@ +// CrossSockets +// Copyright Jeroen Saey +// Created 27-01-2013 +// CrossSocketErrors.cpp + +#include "BaseCrossSocket.h" + +using namespace std; + +CrossSocketErrors::CrossSocketErrors() +{ + be = ok; + _error = ""; + failed_class = NULL; +} + +CrossSocketErrors::CrossSocketErrors(CrossSocketErrorsEnum e) +{ + be = e; + _error = ""; + failed_class = NULL; +} + +string CrossSocketErrors::getError() +{ + return _error; +} + +BaseCrossSocket* CrossSocketErrors::getFailedClass(void) +{ + return failed_class; +} + +void CrossSocketErrors::setErrorString(string msg) +{ + _error = msg; +} + +void CrossSocketErrors::setFailedClass(BaseCrossSocket *pnt) +{ + failed_class = pnt; +} + +bool CrossSocketErrors::operator==(CrossSocketErrors e) +{ + return be == e.be; +} + +bool CrossSocketErrors::operator!=(CrossSocketErrors e) +{ + return be != e.be; +} diff --git a/CrossSocketErrors.h b/CrossSocketErrors.h new file mode 100644 index 0000000..cf7b4fd --- /dev/null +++ b/CrossSocketErrors.h @@ -0,0 +1,53 @@ +// CrossSockets +// Copyright Jeroen Saey +// Created 27-01-2013 +// CrossSocketErrors.h + +#pragma once + +#include + +enum CrossSocketErrorsEnum +{ + ok, // Operation successful + fatal, // Unspecified error + notReady, // The socket was not ready (blockMode) + portInUse, // The specified port is already in use + notConnected, // The socket is invalid or not connected + messageTooLong, // The messageSize is too big to be send + terminated, // Connection terminated (by client) + noResponse, // Cannot connect to client + timeout, // Read/Write operation timeout occurred (in blockingMode) + interrupted // Operation was blocked by signal +}; + +class BaseCrossSocket; + +class CrossSocketErrors +{ +public: + CrossSocketErrors(); + CrossSocketErrors(CrossSocketErrorsEnum e); + + virtual ~CrossSocketErrors(){;} + + virtual std::string getError(); + virtual BaseCrossSocket* getFailedClass(void); + + virtual bool operator == (CrossSocketErrors e); + virtual bool operator != (CrossSocketErrors e); + + virtual void setErrorString(std::string msg); + virtual void setFailedClass(BaseCrossSocket *pnt); +protected: + friend class BaseCrossSocket; + + // The base error type + CrossSocketErrorsEnum be; + + // Human readable error string + std::string _error; + + // A pointer to the class causing the error + BaseCrossSocket *failed_class; +}; diff --git a/CrossSocketPlatform.h b/CrossSocketPlatform.h new file mode 100644 index 0000000..d19b444 --- /dev/null +++ b/CrossSocketPlatform.h @@ -0,0 +1,62 @@ +// CrossSockets +// Copyright Jeroen Saey +// Created 27-01-2013 +// CrossSocketPlatform.h + +#ifdef __WIN32__ +// Define the errorCodes for the Windows platform (taken from CYGWIN error numbers) + #define EOPNOTSUPP WSAEOPNOTSUPP + #define EINTR WSAEINTR + #define EADDRINUSE WSAEADDRINUSE + #define EINPROGRESS WSAEINPROGRESS + #define EWOULDBLOCK WSAEWOULDBLOCK + #define ENOTSOCK WSAENOTSOCK + #define EMSGSIZE WSAEMSGSIZE + #define ETIMEDOUT WSAETIMEDOUT + #define EALREADY WSAEALREADY + #define EBADF WSAEBADF + #define ECONNREFUSED WSAECONNREFUSED + #define ENOTCONN WSAENOTCONN +#else + #include + #include + #include + #include + #include + #include + + #define SOCKETFAIL -1 +#endif + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +// Redefine the socklen_t parameter because it is different on Windows +#ifdef __WIN32__ + #define sw_socklen_t int +#else + #define sw_socklen_t socklen_t +#endif + +using namespace std; + +#ifdef __WIN32__ + +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); +} + +int close(int fd) +{ + return closesocket(fd); +} + +void WSA_exit(void) +{ + WSACleanup(); +} +#endif diff --git a/CrossSockets.h b/CrossSockets.h new file mode 100644 index 0000000..c57ae52 --- /dev/null +++ b/CrossSockets.h @@ -0,0 +1,9 @@ +// CrossSockets +// Copyright Jeroen Saey +// Created 27-01-2013 +// CrossSockets.h + +#pragma once + +#include "BaseCrossSocket.h" +#include "CrossSocket.h" diff --git a/CrossSocketsCore.h b/CrossSocketsCore.h new file mode 100644 index 0000000..40f3f0a --- /dev/null +++ b/CrossSocketsCore.h @@ -0,0 +1,18 @@ +// CrossSockets +// Copyright Jeroen Saey +// Created 27-01-2013 +// CrossSocketsCore.h + +#pragma once + +#ifdef __WIN32__ + #include + + #define F_SETFL FIONBIO + #define O_NONBLOCK 1 +#else + #include + #include + #include + #include +#endif diff --git a/CrossSockets_protocol.pdf b/CrossSockets_protocol.pdf new file mode 100644 index 0000000..fdcc2fc Binary files /dev/null and b/CrossSockets_protocol.pdf differ diff --git a/CrossThreads.cpp b/CrossThreads.cpp new file mode 100644 index 0000000..93edd3c --- /dev/null +++ b/CrossThreads.cpp @@ -0,0 +1,109 @@ +// CrossThreads +// Copyright Jeroen Saey +// Created 29-01-2013 +// CrossThread.cpp + +#include "CrossThreads.h" +#include "errno.h" +#include + +CrossThreads::CrossThreads(threadCallBack callBack, long timeout) : _callBack(callBack), _threadID(0) +#ifdef __WIN32__ +, _timeout(timeout) , _thread(NULL) +#endif +{ +} + +CrossThreads::~CrossThreads() +{ + stop(); +} + +void CrossThreads::stop() +{ + #ifdef __WIN32__ + //waiting for the thread to terminate + if (_thread) + { + if (WAIT_TIMEOUT == ::WaitForSingleObject (_thread, _timeout)) + ::TerminateThread (_thread, 1); + + ::CloseHandle (_thread); + } + #endif +} + +int CrossThreads::create() +{ + +#ifdef __WIN32__ + _thread = ::CreateThread (NULL, 0, + (unsigned long (__stdcall *)(void *))_callBack, + NULL, 0, &_thread); + if (NULL == _thread) + { + break; + } +#endif + + pthread_attr_t attribute; + + // Initialize and set thread and set the attribute + pthread_attr_init(&attribute); + pthread_attr_setdetachstate(&attribute, PTHREAD_CREATE_DETACHED); + + int result; + do + { + result = pthread_create(&_threadID, &attribute, _callBack, (void *)this); + } while (false); + + if (result != 0) + { + std::cout << "CrossThread: Failed to create the thread\n"; + if (result == EAGAIN) + { + std::cout << "The system lacked the necessary resource to create another thread"; + } + else if (result == EINVAL) + { + std::cout << "CrossThread: The value given in the arguments is invalid!"; + } + else + { + std::cout << "CrossThread: The caller does not have the appropriate rights"; + } + } + + // Free the attributes of the thread + pthread_attr_destroy(&attribute); + return result; +} + +void CrossThreads::join() +{ + if (0 != pthread_join(_threadID, NULL)) + { + fprintf(stderr, "pthread_join error\n"); + } +} + +int CrossThreads::remove() +{ + return pthread_detach(_threadID); +} + +unsigned long CrossThreads::getThreadID() +{ + return printf("%lu\n", (unsigned long) _threadID); +} + +void CrossThreads::setThreadCallback(threadCallBack callBack) +{ + _callBack = callBack; +} + +bool CrossThreads::isCreated() +{ + return _threadID ? true : false; +} diff --git a/CrossThreads.h b/CrossThreads.h new file mode 100644 index 0000000..bc87284 --- /dev/null +++ b/CrossThreads.h @@ -0,0 +1,47 @@ +// CrossThreads +// Copyright Jeroen Saey +// Created 29-01-2013 +// CrossThreads.h + +#pragma once + +#ifndef __WIN32__ + +#include "pthread.h" + +typedef pthread_t threadID; +typedef void* (*threadCallBack)(void* parameter); + +#else + +#include +typedef unsigned long (*threadCallBack)(void* parameter); +typedef DWORD threadID; + +#endif + +class CrossThreads +{ + public: + + CrossThreads(threadCallBack callBack = NULL, long timeout = 3000); + virtual ~CrossThreads(); + + void setThreadCallback(threadCallBack callBack); + unsigned long getThreadID(); + int create(); + int remove(); + void join(); + bool isCreated(); + + private: + threadID _threadID; + threadCallBack _callBack; + + #ifdef __WIN32__ + long _timeout; + HANDLE _thread; + #endif + + void stop(); +}; diff --git a/CrossThreadsHandler.cpp b/CrossThreadsHandler.cpp new file mode 100644 index 0000000..7cfc6b9 --- /dev/null +++ b/CrossThreadsHandler.cpp @@ -0,0 +1,58 @@ +// CrossThreadsHandler +// Copyright Jeroen Saey +// Created 29-01-2013 +// CrossThreadsHandler.cpp + +#include "CrossThreadsHandler.h" + +#include +#include +#include + +using namespace std; + +CrossThreadsHandler::CrossThreadsHandler() +{ +} + +unsigned long CrossThreadsHandler::createAndStartThread(threadCallBack callback) +{ + CrossThreads *thread = new CrossThreads(callback); + thread->create(); + _threadList.insert(pair(thread->getThreadID(),thread)); + return thread->getThreadID(); +} + +bool CrossThreadsHandler::joinThread(unsigned long threadID) +{ + map ::iterator iterator = _threadList.find(threadID); + if (iterator != _threadList.end()) + { + iterator->second->join(); + return true; + } + else + { + cout << "The client could not be found in the threadList."; + return false; + } +} + +bool CrossThreadsHandler::removeThread(unsigned long threadID) +{ + map ::iterator iterator = _threadList.find(threadID); + if (iterator != _threadList.end()) + { + _threadList.erase(iterator); + return true; + } + else + { + cout << "The client could not be found in the threadList."; + return false; + } +} + +CrossThreadsHandler::~CrossThreadsHandler() +{ +} diff --git a/CrossThreadsHandler.h b/CrossThreadsHandler.h new file mode 100644 index 0000000..4fe2cea --- /dev/null +++ b/CrossThreadsHandler.h @@ -0,0 +1,25 @@ +// CrossThreadsHandler +// Copyright Jeroen Saey +// Created 29-01-2013 +// CrossThreadsHandler.h + +#pragma once + +#include "CrossThreads.h" + +#include +#include + +class CrossThreadsHandler +{ + public: + CrossThreadsHandler(); + virtual ~CrossThreadsHandler(); + + unsigned long createAndStartThread (threadCallBack callback); + bool removeThread (unsigned long threadID); + bool joinThread (unsigned long threadID); + + private: + std::map _threadList; +}; diff --git a/Example/server.cpp b/Example/server.cpp new file mode 100644 index 0000000..531ee1e --- /dev/null +++ b/Example/server.cpp @@ -0,0 +1,66 @@ +/* + * File: Server.cpp + * Author: SuperSmash + * + * Created on 15 february 2013, 21:10 + */ + +#include "Server.h" +#include "CrossSockets/CrossThreadsHandler.h" + +#include + +using namespace std; + +static Server *self; + +Server::Server() +{ + self = this; + + // Setup the listener into listeningMode and define the serverport + _server.bind(2013); + _server.listen(); + _server.setPassword("welcome"); + + stringstream startMessage; + LogManagement::getInstance()->write(startMessage << "CrossSocket server started.\n" << "PORT: 2013"); + cout << APPLICATIONNAME << " server started.\n"; + + _threadHandler = new CrossThreadsHandler(); + + while (_server.isConnected()) + { + listenForConnections((CrossSocket*)_server.accept()); + } +} + +void Server::listenForConnections(CrossSocket* socket) +{ + _clientSocket = socket; + stringstream welcomeMessage; + welcomeMessage << "Welcome to the CrossSocket server!"; + socket->writeLine(welcomeMessage.str()); + _threadID = _threadHandler->createAndStartThread((threadCallBack) &Server::handleLoopCallback); +} + +void* Server::handleLoopCallback(void *functionPointer) +{ + static_cast (functionPointer)->handleLoop(self->_clientSocket, self->_threadID); +} + +void Server::handleLoop(CrossSocket *socket, unsigned long threadID) +{ + if (self->_server.checkPassword(socket)) + { + while (socket->isConnected()) + { + // Do something here because we got an active connection to the server using CrossSockets ^^ + } + delete socket; + } +} + +Server::~Server() +{ +} diff --git a/Example/server.h b/Example/server.h new file mode 100644 index 0000000..e47a4de --- /dev/null +++ b/Example/server.h @@ -0,0 +1,33 @@ +/* + * File: Server.h + * Author: SuperSmash + * + * Created on 15 february 2013, 21:10 + */ + +#pragma once + +#include "CrossSockets/CrossSockets.h" +#include "CrossSockets/CrossThreads.h" + +class CrossThreadsHandler; + +class Server +{ + public: + Server(); + virtual ~Server(); + + void listenForConnections(CrossSocket* socket); + void deleteClientSocketWithForce(int signalnumber); + + private: + static void* handleLoopCallback(void *functionPointer); + void handleLoop(CrossSocket *socket, unsigned long threadID); + + CrossSocket _server; + CrossThreadsHandler *_threadHandler; + + CrossSocket *_clientSocket; + unsigned long _threadID; +}; diff --git a/README.md b/README.md index 2d9caaa..bb8e8c4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ -# CrossSockets +CrossSockets: -CrossSockets C++ \ No newline at end of file +CrossSockets is a library for C++ to work with the POSIX and Winsock2 sockets. +CrossSockets can help you implement a network communication application. +CrossSockets was created to serve as a client - server communication protocol. + +Example: + +If the server is running you can establish a connection using putty or any other client. +(if you use putty you need to connect to the server using a RAW connection) + +Port: 2013 +Password: welcome