#ifndef __JNET__JSOCKET__ #define __JNET__JSOCKET__ #include #include #include #include #include #include #include #include "JLang/JException.hh" #include "JLang/JFile.hh" #include "JNet/JSocketAddress.hh" /** * \file * Base class for interprocess communication. * * \author mdejong */ namespace JNET {} namespace JPP { using namespace JNET; } namespace JNET { using JLANG::JFile; using JLANG::JSocketException; /** * Socket class. */ class JSocket : public JFile, public JSocketAddress { public: /** * Default constructor. */ JSocket() : JFile() {} /** * Constructor. * * \param domain communication domain * \param type socket type * \param protocol protocol */ JSocket(const int domain, const int type, const int protocol = 0) : JFile(socket(domain, type, protocol)) { if (getFileDescriptor() < 0) { THROW(JSocketException, "Error opening socket " << errno); } setFamily(domain); } /** * Shut down socket. * * \return return value */ int shutdown() { const int value = ::shutdown(getFileDescriptor(), SHUT_RDWR); close(); return value; } /** * Set keep alive of socket. * * \param on true to enable keep alive; false to disable */ void setKeepAlive(const bool on) { setOption(SOL_SOCKET, SO_KEEPALIVE, int(on ? 1 : 0)); } /** * Get keep alive of socket. * * \return true if keep alive; else false */ bool getKeepAlive() const { return (getOption(SOL_SOCKET, SO_KEEPALIVE) == 1); } /** * Set reuse address. * * \param on true to enable reuse address; false to disable */ void setReuseAddress(const bool on) { setOption(SOL_SOCKET, SO_REUSEADDR, int(on ? 1 : 0)); } /** * Get reuse address. * * \return true if enable reuse address; else false */ bool getReuseAddress() const { return (getOption(SOL_SOCKET, SO_REUSEADDR) == 1); } /** * Set receive buffer size. * * \param size number of bytes */ void setReceiveBufferSize(const int size) { setOption(SOL_SOCKET, SO_RCVBUF, int(size)); } /** * Set receive buffer size. * * \return number of bytes */ int getReceiveBufferSize() const { return getOption(SOL_SOCKET, SO_RCVBUF); } /** * Set send buffer size. * * \param size number of bytes */ void setSendBufferSize(const int size) { setOption(SOL_SOCKET, SO_SNDBUF, int(size)); } /** * Get send buffer size. * * \return number of bytes */ int getSendBufferSize() const { return getOption(SOL_SOCKET, SO_SNDBUF); } /** * Read data from socket. * * This method handles I/O errors in such a way that: * -# the return value is set to zero if no data are read; * -# an exception is thrown in case of a fatal error; * * \param buffer buffer * \param length number of bytes to read * \return number of bytes actually read */ virtual int read(char* buffer, const int length) override { int pos = JFile::read(buffer, length); if (pos == 0) { THROW(JSocketException, "Socket read failed " << getFileDescriptor() << ' ' << errno); } else if (pos < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: //case EAGAIN: break; default: THROW(JSocketException, "Socket read error " << getFileDescriptor() << ' ' << errno); } pos = 0; } return pos; } /** * Write data to socket. * * This method handles I/O errors in such a way that: * -# the return value is set to zero if no data are written; * -# an exception is thrown in case of a fatal error; * * \param buffer buffer * \param length number of bytes to write * \return number of bytes actually written */ virtual int write(const char* buffer, const int length) override { int pos = JFile::write(buffer, length); if (pos == 0) { THROW(JSocketException, "Socket write failed " << getFileDescriptor() << ' ' << errno); } else if (pos < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: //case EAGAIN: break; default: THROW(JSocketException, "Socket write error " << getFileDescriptor() << ' ' << errno); } pos = 0; } return pos; } protected: /** * Set socket option. * * \param level level * \param option option * \param value value */ template void setOption(const int level, const int option, const T value) { socklen_t size = sizeof(T); if (setsockopt(getFileDescriptor(), level, option, &value, size) < 0) { THROW(JSocketException, "Set socket option failed " << errno); } } /** * Get socket option. * * \param level level * \param option option * \return value */ template T getOption(const int level, const int option) const { T value; socklen_t size = sizeof(T); if (getsockopt(getFileDescriptor(), level, option, &value, &size) < 0) { THROW(JSocketException, "Get socket option failed " << errno); } return value; } }; } #endif