#ifndef __JNET__JSOCKETCHANNEL__ #define __JNET__JSOCKETCHANNEL__ #include #include #include "JLang/JException.hh" #include "JIO/JByteArrayIO.hh" #include "JNet/JSocketNonblocking.hh" /** * \author mdejong */ namespace JNET {} namespace JPP { using namespace JNET; } namespace JNET { using JLANG::JSocketChannelException; using JIO::JByteArrayReader; using JIO::JByteArrayWriter; /** * Get size of data, including the header. * This method should be implemented for each template class for read operations. * * \param prefix prefix * \return number of bytes */ template int getSizeOfPacket(const JPrefix_t& prefix); /** * Set size of data, including the header. * This method should be implemented for each template class for write operations. * * \param size number of bytes * \param prefix prefix */ template void setSizeOfPacket(const int size, JPrefix_t& prefix); /** * Auxiliary class for socket channel. */ class JSocketChannel { public: enum JType { IO_PREFIX = 1, IO_DATA = 2 }; /** * Reset channel. */ void reset() { type = IO_PREFIX; } protected: JType type; }; /** * Socket input channel. * * This class can be used to read a data packet from a socket without blocking and * to buffer these data for subsequent user read operations. * * The template argument corresponds to a header that precedes any data. * This header acts as a protocol specifier that is used to determine the size of the data packet. * To this end, the method getSizeOfPacket() should be implemented for each header type. * * The reading from the socket may be non-blocking (depending on the configuration of the socket). * The status of this object should be checked for completion of the data packet. * The method reset() should be called before reading the next data packet. */ template class JSocketInputChannel : public JSocketNonblockingReader, public JSocketChannel, public JByteArrayReader { public: using JByteArrayReader::read; /** * Constructor. * * \param socket socket */ JSocketInputChannel(JTCPSocket& socket) : JSocketNonblockingReader(socket), JSocketChannel(), JByteArrayReader() { reset(); } /** * Interruptable read method. * * \return status */ JStatus_t read() { if (type == IO_PREFIX) { if (isReset()) { set((char*) &prefix, sizeof(JPrefix_t)); } if (isBusy()) { JSocketNonblockingReader::read(); } if (isReady()) { buffer.clear(); buffer.resize(getSizeOfPacket(prefix)); memcpy(buffer.data(), &prefix, sizeof(JPrefix_t)); set(buffer.data() + sizeof(JPrefix_t), buffer.size() - sizeof(JPrefix_t)); type = IO_DATA; } } if (type == IO_DATA) { if (isBusy()) { JSocketNonblockingReader::read(); } if (isReady()) { static_cast(*this) = JByteArrayReader(buffer.data(), buffer.size()); } } return JSocketStatus::getStatus(); } /** * Reset channel. * After a reset, the socket channel is ready to receive a new data packet. */ void reset() { JSocketNonblockingReader::reset(); JSocketChannel ::reset(); buffer.clear(); } JPrefix_t prefix; private: std::vector buffer; }; /** * Socket output channel. * * This class can be used to user write to an internal buffer, to format these data as * a packet and to write this packet to the socket without blocking. * * The template argument corresponds to a header that precedes any data. * This header acts as a protocol specifier that is used to determine the size of the data packet. * To this end, the method setSizeOfPacket() should be implemented for each header type. * * The wroting to the socket may be non-blocking (depending on the configuration of the socket). * The status of this object should be checked for completion of the data packet. * The method reset() should be called before writing the next data packet. */ template class JSocketOutputChannel : public JSocketNonblockingWriter, public JSocketChannel, public JByteArrayWriter { public: using JByteArrayWriter::write; /** * Constructor. * * \param socket socket */ JSocketOutputChannel(JTCPSocket& socket) : JSocketNonblockingWriter(socket), JSocketChannel(), JByteArrayWriter() { reset(); } /** * Interruptable write method. * * \return status */ JStatus_t write() { if (type == IO_PREFIX) { const int length = tellp(); setSizeOfPacket(length, prefix); // overwrite prefix seekp(0); JByteArrayWriter::write((char*) &prefix, sizeof(JPrefix_t)); seekp(length); type = IO_DATA; set(data(), tellp()); } if (isBusy()) { JSocketNonblockingWriter::write(); } return JSocketStatus::getStatus(); } /** * Reset channel. * After a reset, the socket channel is ready to receive a new data packet. */ void reset() { JSocketNonblockingWriter::reset(); JSocketChannel ::reset(); // reserve space for prefix resize(sizeof(JPrefix_t)); seekp (sizeof(JPrefix_t)); } JPrefix_t prefix; }; } #endif