Loading...
Searching...
No Matches
Connector.h
Go to the documentation of this file.
// -*- c++ -*-
//------------------------------------------------------------------------------
// Connector.h
//------------------------------------------------------------------------------
// Copyright (C) 1999 Vladislav Grinchenko
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//------------------------------------------------------------------------------
#ifndef CONNECTOR_H
#define CONNECTOR_H
#include <unistd.h> // fcntl(2)
#include <fcntl.h> // fcntl(2)
#if defined(WIN32)
typedef unsigned int socklen_t;
#else
# include <sys/socket.h>
#endif
#include <string.h> // strerror(3)
#include <errno.h> // errno(3)
#include "assa/Logger.h"
#include "assa/EventHandler.h"
#include "assa/Reactor.h"
#include "assa/TimeVal.h"
#include "assa/Address.h"
#include "assa/Socket.h"
namespace ASSA {
enum ConnectMode {
sync,
async
};
template<class SERVICE_HANDLER, class PEER_CONNECTOR>
class Connector : public virtual EventHandler
{
public:
Connector ();
virtual ~Connector ();
virtual int open (const TimeVal& tv_ = TimeVal (5.0),
ConnectMode mode_ = sync,
Reactor* r_ = (Reactor*)NULL);
virtual int close (void);
virtual int connect (SERVICE_HANDLER* sh_,
Address& addr_,
int protocol_ = AF_INET);
virtual int handle_write (int fd);
virtual int handle_timeout (TimerId tid);
protected:
enum ProgressState {
idle,
waiting,
conned,
failed
};
virtual SERVICE_HANDLER* makeServiceHandler (SERVICE_HANDLER* sh_);
virtual int connectServiceHandler (Address& addr, int protocol);
virtual int activateServiceHandler ();
protected:
TimeVal m_timeout;
TimerId m_tid;
Reactor* m_reactor;
ProgressState m_state;
int m_flags;
SERVICE_HANDLER* m_sh;
int m_fd;
ConnectMode m_mode;
private:
void doAsync (void);
int doSync (void);
};
// Convenience definitions
#define SH SERVICE_HANDLER
#define PC PEER_CONNECTOR
//------------------------------------------------------------------------------
// Template member functions definitions
//------------------------------------------------------------------------------
template<class SH, class PC>
Connector<SH, PC>::
Connector ()
: m_tid (0), m_reactor (0), m_state (idle),
m_flags (0), m_sh ((SERVICE_HANDLER*)NULL), m_fd (-1), m_mode (sync)
{
trace_with_mask("Connector::Connector",SOCKTRACE);
set_id ("Connector");
}
template<class SH, class PC>
Connector<SH, PC>::
~Connector ()
{
trace_with_mask("Connector::~Connector",SOCKTRACE);
// If I created SERVICE_HANDLER, should I delete it too?
}
template<class SH, class PC> int
Connector<SH, PC>::
open (const TimeVal& tv_, ConnectMode mode_, Reactor* r_)
{
trace_with_mask("Connector::open", SOCKTRACE);
m_timeout = tv_;
if (async == mode_ && (Reactor*) NULL == r_)
return -1;
m_mode = mode_;
m_reactor = r_;
return 0;
}
template<class SH, class PC> int
Connector<SH, PC>::
close ()
{
trace_with_mask("Connector::close",SOCKTRACE);
return 0;
}
template<class SH, class PC> int
Connector<SH, PC>::
connect (SH* sh_, Address& addr_, int protocol_family_)
{
/*
* We restore socket to its original mode only on
* successful connection. If error occured, client would have
* to close socket anyway.
*
* NOTE: If sh_==0, then result is dangling pointer
* new_sh produced ! Destructor should determine whether
* SERVICE_HANDLER has been created dynamically and if so, delete
* it.
*/
trace_with_mask("Connector::connect",SOCKTRACE);
errno = 0;
m_sh = makeServiceHandler (sh_);
PEER_CONNECTOR& s = *m_sh;
if (addr_.bad ()) {
set_errno (EFAULT); // Bad address
EL((ASSA::ASSAERR,"Bad address (errno %d)\n", errno));
return -1;
}
if (connectServiceHandler (addr_, protocol_family_) == -1)
{
int e = get_errno ();
if (e == EINPROGRESS || e == EWOULDBLOCK)
{
if (async == m_mode) {
doAsync ();
return 0;
}
return doSync ();
}
return -1;
}
return activateServiceHandler ();
}
template<class SH, class PC> SERVICE_HANDLER*
Connector<SH, PC>::
makeServiceHandler (SERVICE_HANDLER* sh_)
{
trace_with_mask("Connector::makeServiceHandler",SOCKTRACE);
SERVICE_HANDLER* new_sh = sh_;
if (sh_ == 0) {
new_sh = new SERVICE_HANDLER;
}
return new_sh;
}
template<class SH, class PC> int
Connector<SH, PC>::
connectServiceHandler (Address& addr_, int protocol_family_)
{
trace_with_mask("Connector::connectServiceHandler",SOCKTRACE);
PEER_CONNECTOR& s = *m_sh;
if ( !s.open (protocol_family_) ) {
EL((ASSA::ASSAERR,"Socket::open (protocol=%d) failed\n",
protocol_family_));
return -1;
}
m_fd = s.getHandler ();
s.setOption (ASSA::Socket::nonblocking, 1);
return (s.connect (addr_) ? 0 : -1);
}
template<class SH, class PC> int
Connector<SH, PC>::
activateServiceHandler ()
{
trace_with_mask("Connector::activateServiceHandler",SOCKTRACE);
return m_sh->open ();
}
template<class SH, class PC> void
Connector<SH, PC>::
doAsync (void)
{
trace_with_mask("Connector::doAsync",SOCKTRACE);
/* We are doing async and 3-way handshake is in
* progress - hook up with Reactor and wait on timer.
* Write event will be our indicator whether connection
* was completed or not.
*/
m_reactor->registerIOHandler (this, m_fd, WRITE_EVENT);
m_tid = m_reactor->registerTimerHandler (this, m_timeout, "ASYNC Connect");
m_state = waiting;
}
template<class SH, class PC> int
Connector<SH, PC>::
doSync (void)
{
trace_with_mask("Connector::doSync",SOCKTRACE);
m_reactor = new Reactor;
m_reactor->registerIOHandler (this, m_fd, WRITE_EVENT);
m_reactor->registerTimerHandler (this, m_timeout, "SYNC Connect");
m_state = waiting;
m_reactor->waitForEvents (&m_timeout); // Let the ball rolling ...
m_reactor->removeHandler (this); // Remove all handlers.
delete m_reactor;
m_reactor = 0;
if (conned == m_state)
{
DL((SOCKTRACE,"Synchronous connect() succeeded.\n"));
return 0;
}
EL((ASSA::ASSAERR,"Synchronous connect() timed out.\n"));
set_errno (ETIMEDOUT);
return -1;
}
template<class SH, class PC> int
Connector<SH, PC>::
handle_write (int fd_)
{
trace_with_mask("Connector::handle_write",SOCKTRACE);
/* Precondition
*/
if (fd_ != m_fd) {
return -1;
}
/* This method serves both sync and async modes - thus the
* differences. For async we remove Timer here. sync runs
* its own private Reactor and handler termination is
* handled in doSync().
*/
if (async == m_mode) { // Complete SH activation
m_reactor->removeTimerHandler (m_tid);
m_tid = 0;
}
/*
* Although SUN and Linux man pages on connect(3) claims that
* "upon asynchronous establishement of connection, select(3)
* will indicate that the file descriptor for the socket is ready
* for writing", as discussed in W.S.Stevens "UNIX network
* programming", Vol I, 2nd edition, BSD-derived systems also
* mark file descriptor both readable and writable when the
* connection establishment encouters an error.
*
* Therefore we need an extra step to find out what really happened.
* One way to do so is to look at socket pending errors...
*/
int error;
int ret;
error = ret = errno = 0;
socklen_t n = sizeof (error);
m_reactor->removeHandler (this, WRITE_EVENT);
#if defined(__CYGWIN32__)
ret = getsockopt (m_fd, SOL_SOCKET, SO_ERROR, (void*)&error, (int*)&n);
#elif defined (WIN32)
ret = getsockopt (m_fd, SOL_SOCKET, SO_ERROR, (char*)&error, (int*)&n);
#else
ret = getsockopt (m_fd, SOL_SOCKET, SO_ERROR, (void*)&error, &n);
#endif
if (ret == 0) {
if (error == 0)
{
if (activateServiceHandler () == 0) {
DL((SOCKTRACE,"Nonblocking connect() completed\n"));
m_state = conned;
}
else {
DL((SOCKTRACE,"Nonblocking connect() failed\n"));
m_state = failed;
}
return (0); // return value doesn't really matter
}
/* Socket pending error - propagate it via errno. */
EL((ASSA::ASSAERR,"Socket pending error: %d\n",error));
set_errno (error);
}
else { /* Solaris pending error. */
EL((ASSA::ASSAERR,"getsockopt(3) = %d\n", ret));
EL((ASSA::ASSAERR,"Solaris pending error!\n"));
}
m_state = failed;
EL((ASSA::ASSAERR,"Nonblocking connect (2) failed\n"));
if (get_errno () == ECONNREFUSED)
{
EL((ASSA::ASSAERR,"Try to compare port "
"numbers on client and service hosts.\n"));
}
/* This is the only way to tell SH that we failed to connect.
*/
if (async == m_mode) {
m_sh->close ();
}
/* Don't alter fd mask - SERVICE_HANDLER::open() could have changed
* it already for application processing needs.
*/
return 0;
}
template<class SH, class PC> int
Connector<SH, PC>::
handle_timeout (TimerId tid_)
{
trace_with_mask("Connector::handle_timeout",SOCKTRACE);
m_state = failed;
set_errno (ETIMEDOUT); // Connection timed out
if (async == m_mode) {
m_reactor->removeHandler (this, WRITE_EVENT);
}
return -1; // Remove Timer Handler
}
} // end namespace ASSA
#endif /* CONNECTOR_H */
SH
#define SH
Definition Acceptor.h:134
Address.h
Address is an abstraction for INET or UNIX-domain address data type.
EventHandler.h
An abstract interface for handling I/O events, timers, and such.
Logger.h
An abstraction to message logging facility.
EL
#define EL(X)
A macro for writing error message to the Logger.
Definition Logger.h:285
DL
#define DL(X)
A macro for writing debug message to the Logger.
Definition Logger.h:273
trace_with_mask
#define trace_with_mask(s, m)
trace_with_mask() is used to trace function call chain in C++ program.
Definition Logger.h:437
Reactor.h
An implementation of Reactor pattern.
Socket.h
Abstraction of socket data type.
TimeVal.h
Class TimeVal is a wrapper around UNIX timeval structure.
ASSA::Address
Definition Address.h:51
ASSA::Address::bad
bool bad() const
Indicates whether there was error during address construction process i.e.
Definition Address.h:80
ASSA::Connector::doAsync
void doAsync(void)
Setup for asynchronous mode completion.
Definition Connector.h:332
ASSA::Connector::close
virtual int close(void)
Do-nothing close.
Definition Connector.h:237
ASSA::Connector::connectServiceHandler
virtual int connectServiceHandler(Address &addr, int protocol)
Default strategy is to make synchronous connection with no timeouts.
Definition Connector.h:303
ASSA::Connector::m_flags
int m_flags
Socket flags (obsolete).
Definition Connector.h:173
ASSA::Connector::ProgressState
ProgressState
state.
Definition Connector.h:131
ASSA::Connector::idle
@ idle
Initialized.
Definition Connector.h:132
ASSA::Connector::failed
@ failed
Failed to connect.
Definition Connector.h:135
ASSA::Connector::conned
@ conned
Connected.
Definition Connector.h:134
ASSA::Connector::waiting
@ waiting
Asynchronously waiting on connection completion.
Definition Connector.h:133
ASSA::Connector::m_timeout
TimeVal m_timeout
Timeout.
Definition Connector.h:161
ASSA::Connector::activateServiceHandler
virtual int activateServiceHandler()
Activate handler by calling its open() method.
Definition Connector.h:323
ASSA::Connector::open
virtual int open(const TimeVal &tv_=TimeVal(5.0), ConnectMode mode_=sync, Reactor *r_=(Reactor *) NULL)
Configure Connector.
Definition Connector.h:223
ASSA::Connector::connect
virtual int connect(SERVICE_HANDLER *sh_, Address &addr_, int protocol_=AF_INET)
Define strategy for establishing connection.
Definition Connector.h:245
ASSA::Connector::handle_write
virtual int handle_write(int fd)
Handle connection completion.
Definition Connector.h:378
ASSA::Connector::~Connector
virtual ~Connector()
Destructor. Do-nothing.
Definition Connector.h:215
ASSA::Connector::makeServiceHandler
virtual SERVICE_HANDLER * makeServiceHandler(SERVICE_HANDLER *sh_)
Defines creation strategy for ServiceHandler.
Definition Connector.h:289
ASSA::Connector::Connector
Connector()
Constructor. Do-nothing.
Definition Connector.h:205
ASSA::Connector::m_mode
ConnectMode m_mode
Mode (sync/async).
Definition Connector.h:182
ASSA::Connector::m_state
ProgressState m_state
Connection progress state.
Definition Connector.h:170
ASSA::Connector::m_sh
SERVICE_HANDLER * m_sh
Reference to ServiceHandler.
Definition Connector.h:176
ASSA::Connector::handle_timeout
virtual int handle_timeout(TimerId tid)
Handler connection timeout.
Definition Connector.h:474
ASSA::Connector::m_tid
TimerId m_tid
Timer id.
Definition Connector.h:164
ASSA::Connector::doSync
int doSync(void)
Synchronous mode completion.
Definition Connector.h:349
ASSA::Connector::m_fd
int m_fd
Socket file descriptor.
Definition Connector.h:179
ASSA::Connector::m_reactor
Reactor * m_reactor
Reference to Reactor (for async).
Definition Connector.h:167
ASSA::EventHandler::set_id
void set_id(const std::string &id_)
Set EventHandler ID.
Definition EventHandler.h:153
ASSA::EventHandler::EventHandler
EventHandler()
Constructor.
Definition EventHandler.h:165
ASSA::Reactor
Definition Reactor.h:58
ASSA::Socket::nonblocking
@ nonblocking
Set Socket to a non-blocking mode (O_RDWR|O_NONBLOCK).
Definition Socket.h:115
ASSA::TimeVal
Definition TimeVal.h:31
ASSA
Definition Acceptor.h:40
ASSA::WRITE_EVENT
@ WRITE_EVENT
Notify when there will be room for at least 1 byte to be written to IO channel without blocking.
Definition EventHandler.h:39
ASSA::set_errno
void set_errno(int new_errno_)
Set error number in a portable way.
Definition Logger_Impl.h:128
ASSA::TimerId
unsigned long TimerId
Timer Id is used in handle_timeout() calls.
Definition EventHandler.h:27
ASSA::SOCKTRACE
@ SOCKTRACE
Extended Socket & friends messages.
Definition LogMask.h:42
ASSA::ASSAERR
@ ASSAERR
ASSA and system errors.
Definition LogMask.h:34
ASSA::get_errno
int get_errno()
Fetch error number in a portable way.
Definition Logger_Impl.h:115
ASSA::ConnectMode
ConnectMode
Definition Connector.h:43
ASSA::sync
@ sync
Synchronous connection mode.
Definition Connector.h:44
ASSA::async
@ async
Asynchronous connection mode.
Definition Connector.h:45