{
struct in_addr in;
in.s_addr = htonl(source());
- std::string src (inet_ntoa(in));
+ char buffer[128];
+ std::string src (inet_ntop(AF_INET,&in,buffer,128));
in.s_addr = htonl(destination());
- std::string dst (inet_ntoa(in));
+ std::string dst (inet_ntop(AF_INET,&in,buffer,128));
os << "Internet protocol Version 4:\n"
<< " version : " << version() << "\n"
<< " IHL : " << ihl() << "\n"
prefix_ senf::Packet::~Packet()
{
- /** \fixme This is bad ... we cannot check this since this
- assertion fails at the moment if the Packet constructor throws
- ... hrmpf ... we really need to initialize refcount_ to 0 and
+ /** \todo This is sad ... we cannot check this since this assertion fails at the moment if the
+ Packet constructor throws ... hrmpf ... we really should initialize refcount_ to 0 and
remove the 'false' argument to the ptr constructor in create */
// BOOST_ASSERT( !this->refcount_ && !this->impl_ );
SATCOM_PKF_REFC_MSG("] Packet::~Packet (" << this << ")\n");
template <class Value>
prefix_ void
senf::Parse_ListS_wrapper<Parser,Sentinel,Container>::insert(iterator pos,
- Value const & t)
+ Value const & t)
{
- /** \fixme What, if pos == end() / default constructed iterator ? */
size_type ix (pos.raw()-container_.begin());
container_.insert(pos.raw(),t.bytes(),0);
Parser(container_.begin()+ix).value(t);
template <class Value>
prefix_ void
senf::Parse_ListS_wrapper<Parser,Sentinel,Container>::insert(iterator pos, size_type n,
- Value const & t)
+ Value const & t)
{
size_type ix (pos.raw()-container_.begin());
container_.insert(pos.raw(),n*t.bytes(),0);
template <class InputIterator>
prefix_ void
senf::Parse_ListS_wrapper<Parser,Sentinel,Container>::insert(iterator pos,
- InputIterator f,
- InputIterator l)
+ InputIterator f,
+ InputIterator l)
{
/** \todo Optimize this for random-access and multi-pass iterators */
for (;f!=l;++f,++pos) insert(pos,*f);
prefix_ void senf::ReadHelper<Handle>::process(Handle handle,
senf::Scheduler::EventId event)
{
- /** \fixme Move the done() calls to outside the try/catch block */
try {
if (event != senf::Scheduler::EV_READ)
throw SystemException(EPIPE);
tail_.assign(data_,n,std::string::npos);
data_.erase(n);
}
- done();
}
}
catch (senf::SystemException const & ex) {
errno_ = ex.err;
done();
+ return;
}
+ if (complete_)
+ done();
}
template <class Handle>
if (eventMask & EV_READ) i->second.cb_read = cb;
if (eventMask & EV_PRIO) i->second.cb_prio = cb;
if (eventMask & EV_WRITE) i->second.cb_write = cb;
- if (eventMask & EV_HUP) i->second.cb_hup = cb;
- if (eventMask & EV_ERR) i->second.cb_err = cb;
epoll_event ev;
memset(&ev,0,sizeof(ev));
if (eventMask & EV_READ) i->second.cb_read = 0;
if (eventMask & EV_PRIO) i->second.cb_prio = 0;
if (eventMask & EV_WRITE) i->second.cb_write = 0;
- if (eventMask & EV_HUP) i->second.cb_hup = 0;
- if (eventMask & EV_ERR) i->second.cb_err = 0;
epoll_event ev;
memset(&ev,0,sizeof(ev));
if (cb_read) mask |= EPOLLIN;
if (cb_prio) mask |= EPOLLPRI;
if (cb_write) mask |= EPOLLOUT;
- if (cb_hup) mask |= EPOLLHUP;
- if (cb_err) mask |= EPOLLERR;
return mask;
}
struct epoll_event ev;
int events = epoll_wait(epollFd_, &ev, 1, timeout);
if (events<0)
- // Hmm ... man epoll says, it will NOT return with EINTR ??
+ // Hmm ... man epoll says, it will NOT return with EINTR. I hope, this is true :-)
throw SystemException(errno);
if (events==0)
- // Timeout .. it will be run when reachiung the top of the loop
+ // Timeout .. the handler will be run when going back to the loop top
continue;
FdTable::iterator i = fdTable_.find(ev.data.fd);
BOOST_ASSERT (i != fdTable_.end() );
- EventSpec const & spec (i->second);
+ // \todo Make this more efficient. Instead of copying the event-spec it should be
+ // revalidated by monitoring add/remove calls
+ EventSpec spec (i->second);
+
+ unsigned extraFlags (0);
+ if (ev.events & EPOLLHUP) extraFlags |= EV_HUP;
+ if (ev.events & EPOLLERR) extraFlags |= EV_ERR;
if (ev.events & EPOLLIN) {
BOOST_ASSERT(spec.cb_read);
- spec.cb_read(EV_READ);
+ spec.cb_read(EventId(EV_READ | extraFlags));
}
else if (ev.events & EPOLLPRI) {
BOOST_ASSERT(spec.cb_prio);
- spec.cb_prio(EV_PRIO);
+ spec.cb_prio(EventId(EV_PRIO | extraFlags));
}
else if (ev.events & EPOLLOUT) {
BOOST_ASSERT(spec.cb_write);
- spec.cb_write(EV_WRITE);
- }
-
- else if (ev.events & EPOLLHUP) {
- if (spec.cb_hup)
- spec.cb_hup(EV_HUP);
- else if (ev.events & EPOLLERR) {
- /** \fixme This is stupid, if cb_write and cb_read are
- the same. The same below. We really have to
- exactly define sane semantics of what to do on
- EPOLLHUP and EPOLLERR. */
- if (spec.cb_write) spec.cb_write(EV_HUP);
- if (spec.cb_read) spec.cb_read(EV_HUP);
- }
+ spec.cb_write(EventId(EV_WRITE | extraFlags));
}
- else if (ev.events & EPOLLERR && ! ev.events & EPOLLHUP) {
- if (spec.cb_err)
- spec.cb_err(EV_ERR);
- else {
- if (spec.cb_write) spec.cb_write(EV_ERR);
- if (spec.cb_read) spec.cb_read(EV_ERR);
- }
+ else {
+ // This branch is only taken, if HUP or ERR is signaled but none of IN/OUT/PRI.
+ // In this case we will signal all registered callbacks. The callbacks must be
+ // prepared to be called multiple times if they are registered to more than
+ // one event.
+ if (spec.cb_write)
+ spec.cb_write(EventId(extraFlags));
+ if (spec.cb_prio)
+ spec.cb_prio(EventId(extraFlags));
+ if (spec.cb_read)
+ spec.cb_read(EventId(extraFlags));
}
-
}
}
/// \brief Types of file descriptor events */
enum EventId { EV_NONE=0,
- EV_READ=1, EV_PRIO=2, EV_WRITE=4, EV_HUP=8, EV_ERR=16,
- EV_ALL=31 };
+ EV_READ=1, EV_PRIO=2, EV_WRITE=4,
+ EV_ALL=7,
+ EV_HUP=8, EV_ERR=16 };
/** \brief Template typedef for Callback type
SimpleCallback cb_read;
SimpleCallback cb_prio;
SimpleCallback cb_write;
- SimpleCallback cb_hup;
- SimpleCallback cb_err;
int epollMask() const;
};
void callback(int fd, Scheduler::EventId ev)
{
event = ev;
- switch (event) {
+ switch (event & Scheduler::EV_ALL) {
case Scheduler::EV_READ:
size = recv(fd,buffer,1024,0);
break;
size = write(fd,buffer,size);
Scheduler::instance().terminate();
break;
- case Scheduler::EV_HUP:
- case Scheduler::EV_ERR:
- case Scheduler::EV_NONE:
- case Scheduler::EV_ALL:
- ;
}
Scheduler::instance().terminate();
}
BOOST_CHECK_NO_THROW( Scheduler::instance().remove(handle,Scheduler::EV_WRITE) );
event = Scheduler::EV_NONE;
BOOST_CHECK_NO_THROW( Scheduler::instance().process() );
- BOOST_CHECK_EQUAL( event, Scheduler::EV_READ );
+ BOOST_CHECK_EQUAL( event, Scheduler::EventId(Scheduler::EV_READ|Scheduler::EV_HUP) );
BOOST_REQUIRE_EQUAL( size, 2 );
buffer[size]=0;
BOOST_CHECK_EQUAL( buffer, "OK" );
prefix_ void senf::WriteHelper<Handle>::process(Handle handle,
senf::Scheduler::EventId event)
{
- /** \fixme Move the done() calls to outside the try/catch block */
+ bool complete_ (false);
try {
if (event != senf::Scheduler::EV_WRITE)
throw senf::SystemException(EPIPE);
offset_ += handle.write(data_.data()+offset_,data_.size()-offset_);
if (offset_ >= data_.size()) {
data_.erase();
- done();
+ complete_ = true;
}
}
catch (senf::SystemException const & ex) {
errno_ = ex.err;
done();
+ return;
}
+ if (complete_)
+ done();
}
template <class Handle>
// structors
template <class Policy>
+prefix_ senf::ClientSocketHandle<Policy>::ClientSocketHandle()
+{}
+
+template <class Policy>
template <class OtherPolicy>
prefix_ senf::ClientSocketHandle<Policy>::
ClientSocketHandle(ClientSocketHandle<OtherPolicy> other,
typelist of Poclicy classes which can be accessed. You use protocol<ProtocolClass>() to
access a protocol class. \c Policies can of course be underspecified or even empty.
- \idea add more flexible read/write members for a) boost::arrays and arrays of other types b)
- std::vector (which uses contiguous memory ..) c) other random-access containers (we should
- use some configurable trait class to identify containers with contiguous storage). Probably
- we should just use a generic Boost.Range interface. Here we again come to the point: make
- all except the most basic members be non-member algorithms ? this would make the
- configuration of such extenden members more flexible.
-
\see \ref policy_group \n
\ref protocol_group
*/
///\name Structors and default members
///@{
- // no default constructor
+ // default default constructor
// default copy constructor
// default copy assignment
// default destructor
+ // here to implement
+ ClientSocketHandle();
+
// conversion constructors
template <class OtherPolicy>
ClientSocketHandle(ClientSocketHandle<OtherPolicy> other,
\param[in/out] range Range to store data in
\returns past-the-end iterator pointer to after the
last read character
- \see \ref read() */
+ \see \ref read() \n
+ <a href="http://www.boost.org/libs/range/index.html">Boost.Range</a> */
template <class ForwardWritableRange>
typename boost::range_iterator<ForwardWritableRange>::type
read (ForwardWritableRange & range);
///< Read data into range
- /**< \see
- read(ForwardWritableRange const &) \n
- read() */
+ /**< \see read(ForwardWritableRange const &) \n
+ read() \n
+ <a href="http://www.boost.org/libs/range/index.html">Boost.Range</a> */
template <class Sequence>
void read (Sequence & container, unsigned limit);
///< Read data into container
received
\returns past-the-end iterator pointer to after the
last read character
- \see \ref readfrom() */
+ \see \ref readfrom() \n
+ <a href="http://www.boost.org/libs/range/index.html">Boost.Range</a> */
template <class ForwardWritableRange>
typename boost::range_iterator<ForwardWritableRange>::type
readfrom (ForwardWritableRange & range, Address & from);
///< Read data into range
- /**< \see
- readfrom(ForwardWritableRange const&,Address&) \n
- readfrom() */
+ /**< \see readfrom(ForwardWritableRange const&,Address&) \n
+ readfrom() \n
+ <a href="http://www.boost.org/libs/range/index.html">Boost.Range</a> */
template <class Sequence>
void readfrom (Sequence & container, Address & from, unsigned limit);
///< Read data into container
/**< \param[in] start beginning of area to write
\param[in] end past-the-end pointer to area to write
\returns past-the-end pointer after last byte written
- \see \ref write() */
+ \see \ref write() \n
+ <a href="http://www.boost.org/libs/range/index.html">Boost.Range</a> */
/** \brief Write data to unconnected socket
\param[in] start address of buffer to write
\param[in] end past-the-end pointer after data to write
\returns past-the-end iterator after last byte written
- \see \ref writeto() */
+ \see \ref writeto() \n
+ <a href="http://www.boost.org/libs/range/index.html">Boost.Range</a> */
///////////////////////////////////////////////////////////////////////////
///\name Addressing
#define prefix_
///////////////////////////////cc.p////////////////////////////////////////
-prefix_ void senf::ConnectedCommunicationPolicy::listen(FileHandle handle,
- unsigned backlog)
+prefix_ void senf::ConnectedCommunicationPolicy::do_listen(FileHandle handle,
+ unsigned backlog)
{
::listen(handle.fd(),backlog);
}
///////////////////////////////cti.p///////////////////////////////////////
template <class Policy>
+prefix_ void senf::ConnectedCommunicationPolicy::
+listen(ServerSocketHandle<Policy> handle, unsigned backlog,
+ typename IfAddressingPolicyIsNot<Policy,NoAddressingPolicy>::type *)
+{
+ do_listen(handle, backlog);
+}
+
+template <class Policy>
prefix_ int senf::ConnectedCommunicationPolicy::
accept(ServerSocketHandle<Policy> handle,
typename ServerSocketHandle<Policy>::Address & address,
*/
struct ConnectedCommunicationPolicy : public CommunicationPolicyBase
{
- static void listen(FileHandle handle, unsigned backlog);
+ template <class Policy>
+ static void listen(ServerSocketHandle<Policy> handle, unsigned backlog,
+ typename IfAddressingPolicyIsNot<Policy,NoAddressingPolicy>::type * = 0);
///< Enable establishing new connections on the socket
/**< \param[in] handle socket handle to enable reception on
- \param[in] backlog size of backlog queue
-
- \fixme listen probably makes no sense without accept,
- so listen() should depend on AddressingPolicy
- too. */
+ \param[in] backlog size of backlog queue */
template <class Policy>
static int accept(ServerSocketHandle<Policy> handle,
typename ServerSocketHandle<Policy>::Address & address,
peer
\returns file descriptor of new client socket */
private:
+ static void do_listen(FileHandle handle, unsigned backlog);
static int do_accept(FileHandle handle, struct sockaddr * addr, unsigned len);
};
prefix_ bool senf::FileHandle::valid()
const
{
- return body().valid();
+ return body_ && body().valid();
}
prefix_ bool senf::FileHandle::boolean_test()
return body().fd();
}
+prefix_ senf::FileHandle::FileHandle()
+ : body_(0)
+{}
+
prefix_ senf::FileHandle::FileHandle(std::auto_ptr<FileBody> body)
: body_(body.release())
{}
prefix_ senf::FileBody & senf::FileHandle::body()
{
+ BOOST_ASSERT(body_);
return *body_;
}
prefix_ senf::FileBody const & senf::FileHandle::body()
const
{
+ BOOST_ASSERT(body_);
return *body_;
}
\image html FhHierarchy.png
- The senf::FileHandle class is the base of a hierarchy of socket
- handle classes (realized as templates). These classes provide an
- interface to the complete socket API. While going down the
- inheritance hierarchy, the interface will be more and more
- complete.
-
- The most complete interface is provided by
- senf::ProtocolClientSocketHandle and
- senf::ProtocolServerSocketHandle. The template Arguments specifies
- the Protocol class of the underlying socket type. These are the
- \e only classes having public constructors and are therefore the
- only classes, which may be created by the library user. You will
- normally use these classes by naming a specific socket typedef
- (e.g. senf::TCPv4ClientSocketHandle).
-
- However, to aid writing flexible and generic code, the socket
- library provides the senf::ClientSocketHandle and
- senf::ServerSocketHandle class templates. These templates
- implement a family of closely related classes based on the
- specification of the socket policy. This policy specification may
- be \e incomplete (see below). Instances of
- senf::ClientSocketHandle/senf::ServerSocketHandle can be assigned
- and converted to different ClientSocketHandle/ServerSocketHandle
- types as long as the policy specifications are compatible.
-
- \attention It is very important, to (almost) always pass the socket
- handle <em>by value</em>. The socket handle is a very lightweight
- class and designed to be used like an ordinary built-in type. This
- is very important in combination with the policy interface.
-
- \note The FileHandle hierarchy below the SocketHandle template is
- \e not meant to be user extensible. To add new socket types, you
- should introduce new protocol and/or policy classes, the
- SocketHandle classes should not be changed.
+ The senf::FileHandle class is the base of a hierarchy of socket handle classes (realized as
+ templates). These classes provide an interface to the complete socket API. While going down the
+ inheritance hierarchy, the interface will be more and more complete.
+
+ The most complete interface is provided by senf::ProtocolClientSocketHandle and
+ senf::ProtocolServerSocketHandle. The template Arguments specifies the Protocol class of the
+ underlying socket type. These are the \e only classes having public constructors and are
+ therefore the only classes, which may be created by the library user. You will normally use
+ these classes by naming a specific socket typedef (e.g. senf::TCPv4ClientSocketHandle).
+
+ However, to aid writing flexible and generic code, the socket library provides the
+ senf::ClientSocketHandle and senf::ServerSocketHandle class templates. These templates implement
+ a family of closely related classes based on the specification of the socket policy. This policy
+ specification may be \e incomplete (see below). Instances of
+ senf::ClientSocketHandle/senf::ServerSocketHandle can be assigned and converted to different
+ ClientSocketHandle/ServerSocketHandle types as long as the policy specifications are compatible.
+
+ \attention It is very important, to (almost) always pass the socket handle <em>by
+ value</em>. The socket handle is a very lightweight class and designed to be used like an
+ ordinary built-in type. This is very important in combination with the policy interface.
+
+ \note The FileHandle hierarchy below the SocketHandle template is \e not meant to be user
+ extensible. To add new socket types, you should introduce new protocol and/or policy classes,
+ the SocketHandle classes should not be changed.
*/
#ifndef HH_FileHandle_
will have to call the protected FileHandle constructor passing a new senf::FileBody
instance. This instance may either be a simple senf::FileBody or a class derived from
senf::FileBody.
-
- \fixme Add public default constructor to allow declaration of (empty) senf::FileHandle
- variables.
*/
class FileHandle
: public SafeBool<FileHandle>
///\name Structors and default members
///@{
- // protected default constructor
+ FileHandle();
+
+ // my default constructor
// default copy constructor
// default copy assignment
// default destructor
class FHandle : public senf::FileHandle
{
public:
- FHandle(int fd=-1)
+ FHandle() {}
+ FHandle(int fd)
: senf::FileHandle(std::auto_ptr<senf::FileBody>(
new senf::FileBody(fd))) {}
FHandle(std::string name)
BOOST_CHECK(fh);
BOOST_CHECK(!!fh);
- FHandle fh2(fh);
+ FHandle fh2;
+ BOOST_CHECK( ! fh2.valid() );
+ fh2 = fh;
BOOST_CHECK_EQUAL(fh.fd(), fh2.fd());
BOOST_CHECK(fh.writeable());
prefix_ std::string senf::INet4Address::host()
const
{
- /** \fixme thread safety? */
- return std::string(::inet_ntoa(addr_.sin_addr));
+ char buffer[128];
+ return std::string(::inet_ntop(AF_INET,&addr_.sin_addr,buffer,128));
}
prefix_ unsigned senf::INet4Address::port()
INet4Address();
INet4Address(char const * address); ///< Set address and port
/**< See INet4Address(std::string)
- \throws InvalidINetAddressException
- \fixme Why do I need this version? Shouldn't the
- std::string version be enough ? */
+ \throws InvalidINetAddressException */
INet4Address(std::string address); ///< Set address and port
/**< This constructor expects a string of the form
'xxx.xxx.xxx.xxx:pppp'. The constructor will use this
///////////////////////////////cti.p///////////////////////////////////////
template <class SocketProtocol>
+prefix_ senf::ProtocolClientSocketHandle<SocketProtocol>::
+ProtocolClientSocketHandle(UninitializedType)
+{}
+
+template <class SocketProtocol>
prefix_ senf::ProtocolClientSocketHandle<SocketProtocol>::ProtocolClientSocketHandle()
: ClientSocketHandle<typename SocketProtocol::Policy>(
std::auto_ptr<senf::SocketProtocol>(new SocketProtocol()))
// Types
typedef SocketProtocol Protocol; ///< The sockets protocol
+ enum UninitializedType { Uninitialized }; ///< Flag to call 'uninitialized' constructor
///////////////////////////////////////////////////////////////////////////
///\name Structors and default members
# define BOOST_PP_ITERATION_PARAMS_1 (4, (1, 9, "Socket/ProtocolClientSocketHandle.mpp", 1))
# include BOOST_PP_ITERATE()
+ /** \brief Create uninitialized socket variable
+
+ This special constructor is called when passing
+ ProtocolClientSocketHandle::Uninitialized as only argument to the constructor. This will
+ create an in-\ref valid() socket handle which can however be assigned later with another
+ socket instance.
+
+ \implementation The socket handle will have no \c body allocated.
+ */
+ ProtocolClientSocketHandle(UninitializedType);
+
///@}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////cti.p///////////////////////////////////////
template <class SocketProtocol>
+prefix_ senf::ProtocolServerSocketHandle<SocketProtocol>::
+ProtocolServerSocketHandle(UninitializedType)
+{}
+
+template <class SocketProtocol>
prefix_ senf::ProtocolServerSocketHandle<SocketProtocol>::ProtocolServerSocketHandle()
: ServerSocketHandle<typename SocketProtocol::Policy>(
std::auto_ptr<senf::SocketProtocol>(new SocketProtocol()))
// Types
typedef SocketProtocol Protocol; ///< The socket protocol
+ enum UninitializedType { Uninitialized }; ///< Flag to call 'uninitialized' constructor
///////////////////////////////////////////////////////////////////////////
///\name Structors and default members
# define BOOST_PP_ITERATION_PARAMS_1 (4, (1, 9, "Socket/ProtocolServerSocketHandle.mpp", 1))
# include BOOST_PP_ITERATE()
+ /** \brief Create uninitialized socket variable
+
+ This special constructor is called when passing
+ ProtocolServerSocketHandle::Uninitialized as only argument to the constructor. This will
+ create an in-\ref valid() socket handle which can however be assigned later with another
+ socket instance.
+
+ \implementation The socket handle will have no \c body allocated.
+ */
+ ProtocolServerSocketHandle(UninitializedType);
///@}
///////////////////////////////////////////////////////////////////////////
#define prefix_ inline
///////////////////////////////cti.p///////////////////////////////////////
+template <class Policy>
+prefix_ senf::ServerSocketHandle<Policy>::ServerSocketHandle()
+{}
+
template <class SocketPolicy>
template <class OtherPolicy>
prefix_ senf::ServerSocketHandle<SocketPolicy>::
///\name Structors and default members
///@{
- // no default constructor
+ // default default constructor
// default copy constructor
// default copy assignment
// default destructor
+ // here to implement
+ ServerSocketHandle();
+
// conversion constructors
template <class OtherPolicy>
ServerSocketHandle(ServerSocketHandle<OtherPolicy> other,
///////////////////////////////cti.p///////////////////////////////////////
template <class SocketPolicy>
+prefix_ senf::SocketHandle<SocketPolicy>::SocketHandle()
+{}
+
+template <class SocketPolicy>
template <class OtherPolicy>
prefix_ senf::SocketHandle<SocketPolicy>::SocketHandle(SocketHandle<OtherPolicy> other,
typename IsCompatible<OtherPolicy>::type *)
///\name Structors and default members
///@{
+ // default default constructor
// default copy constructor
// default copy assignment
// default destructor
+ // here to implement
+ SocketHandle();
+
// conversion constructors
template <class OtherPolicy>