/home/vlg/develop/libASSA/libassa/assa/Connector.h

Go to the documentation of this file.
00001 // -*- c++ -*-
00002 //------------------------------------------------------------------------------
00003 //                            Connector.h
00004 //------------------------------------------------------------------------------
00005 //  Copyright (C) 1999  Vladislav Grinchenko 
00006 //
00007 //  This library is free software; you can redistribute it and/or
00008 //  modify it under the terms of the GNU Library General Public
00009 //  License as published by the Free Software Foundation; either
00010 //  version 2 of the License, or (at your option) any later version.
00011 //------------------------------------------------------------------------------
00012 #ifndef CONNECTOR_H
00013 #define CONNECTOR_H
00014 
00015 #include <unistd.h>     // fcntl(2)
00016 #include <fcntl.h>      // fcntl(2)
00017 
00018 #if defined(WIN32)
00019 typedef unsigned int socklen_t;
00020 #else
00021 #   include <sys/socket.h>  
00022 #endif
00023 
00024 #include <string.h>     // strerror(3)
00025 #include <errno.h>      // errno(3)
00026 
00027 #include "assa/Logger.h"
00028 #include "assa/EventHandler.h"
00029 #include "assa/Reactor.h"
00030 #include "assa/TimeVal.h"
00031 #include "assa/Address.h"
00032 #include "assa/Socket.h"
00033 
00034 namespace ASSA {
00035 
00043 enum ConnectMode { 
00044     sync,                   
00045     async                   
00046 };
00047 
00062 template<class SERVICE_HANDLER, class PEER_CONNECTOR>
00063 class Connector : public virtual EventHandler
00064 {
00065 public:
00067     Connector ();
00068 
00070     virtual ~Connector ();
00071     
00083     virtual int open (const TimeVal& tv_ = TimeVal (5.0), 
00084                       ConnectMode mode_ = sync,
00085                       Reactor* r_ = (Reactor*)NULL);
00086 
00091     virtual int close (void);
00092 
00117     virtual int connect (SERVICE_HANDLER* sh_, 
00118                          Address& addr_, 
00119                          int protocol_ = AF_INET);
00120 
00122     virtual int handle_write (int fd);
00123 
00125     virtual int handle_timeout (TimerId tid);
00126 
00127 protected:
00131     enum ProgressState { 
00132         idle,       
00133         waiting,    
00134         conned,     
00135         failed      
00136     };
00137 
00145     virtual SERVICE_HANDLER* makeServiceHandler (SERVICE_HANDLER* sh_);
00146 
00152     virtual int connectServiceHandler (Address& addr, int protocol);
00153 
00157     virtual int activateServiceHandler ();
00158 
00159 protected:
00161     TimeVal m_timeout;
00162 
00164     TimerId m_tid;
00165 
00167     Reactor* m_reactor;
00168     
00170     ProgressState m_state;
00171 
00173     int m_flags;
00174 
00176     SERVICE_HANDLER* m_sh;
00177 
00179     int m_fd;
00180 
00182     ConnectMode m_mode;
00183 
00184 private:
00186     void doAsync (void);
00187 
00191     int doSync (void);
00192 };
00193 
00194 // Convenience definitions
00195 
00196 #define SH SERVICE_HANDLER
00197 #define PC PEER_CONNECTOR
00198 
00199 //------------------------------------------------------------------------------
00200 // Template member functions definitions
00201 //------------------------------------------------------------------------------
00202 
00203 template<class SH, class PC> 
00204 Connector<SH, PC>::
00205 Connector () 
00206     : m_tid (0), m_reactor (0), m_state (idle),
00207       m_flags (0), m_sh ((SERVICE_HANDLER*)NULL), m_fd (-1), m_mode (sync)
00208 {
00209     trace_with_mask("Connector::Connector",SOCKTRACE);
00210     set_id ("Connector");
00211 }
00212 
00213 template<class SH, class PC> 
00214 Connector<SH, PC>::
00215 ~Connector () 
00216 {
00217     trace_with_mask("Connector::~Connector",SOCKTRACE);
00218     // If I created SERVICE_HANDLER, should I delete it too?
00219 }
00220 
00221 template<class SH, class PC> int
00222 Connector<SH, PC>::
00223 open (const TimeVal& tv_, ConnectMode mode_, Reactor* r_) 
00224 {
00225     trace_with_mask("Connector::open", SOCKTRACE);
00226 
00227     m_timeout = tv_;
00228     if (async == mode_ && (Reactor*) NULL == r_) 
00229         return -1;
00230     m_mode = mode_;
00231     m_reactor = r_;
00232     return 0;
00233 }
00234 
00235 template<class SH, class PC> int
00236 Connector<SH, PC>::
00237 close () 
00238 {
00239     trace_with_mask("Connector::close",SOCKTRACE);
00240     return 0;
00241 }
00242 
00243 template<class SH, class PC> int
00244 Connector<SH, PC>::
00245 connect (SH* sh_, Address& addr_, int protocol_family_)
00246 {
00247     /*
00248      * We restore socket to its original mode only on 
00249      * successful connection. If error occured, client would have
00250      * to close socket anyway.
00251      *
00252      * NOTE: If sh_==0, then result is dangling pointer
00253      * new_sh produced ! Destructor should determine whether 
00254      * SERVICE_HANDLER has been created dynamically and if so, delete
00255      * it.
00256      */
00257     trace_with_mask("Connector::connect",SOCKTRACE);
00258     errno = 0;
00259 
00260     m_sh = makeServiceHandler (sh_);
00261     PEER_CONNECTOR& s = *m_sh;
00262 
00263     if (addr_.bad ()) {
00264         set_errno (EFAULT); // Bad address
00265         EL((ASSA::ASSAERR,"Bad address (errno %d)\n", errno));
00266         return -1;
00267     }
00268 
00269     if (connectServiceHandler (addr_, protocol_family_) == -1) 
00270     {
00271         int e = get_errno ();
00272         if (e == EINPROGRESS || e == EWOULDBLOCK) 
00273         {
00274             if (async == m_mode) { 
00275                 doAsync (); 
00276                 return 0; 
00277             }
00278 
00279             return doSync ();
00280         }
00281         return -1;
00282     }
00283 
00284     return activateServiceHandler ();
00285 }
00286 
00287 template<class SH, class PC> SERVICE_HANDLER*
00288 Connector<SH, PC>::
00289 makeServiceHandler (SERVICE_HANDLER* sh_) 
00290 {
00291     trace_with_mask("Connector::makeServiceHandler",SOCKTRACE);
00292 
00293     SERVICE_HANDLER* new_sh = sh_;
00294 
00295     if (sh_ == 0) {
00296         new_sh = new SERVICE_HANDLER;
00297     }
00298     return new_sh;
00299 }
00300 
00301 template<class SH, class PC> int
00302 Connector<SH, PC>::
00303 connectServiceHandler (Address& addr_, int protocol_family_)
00304 {
00305     trace_with_mask("Connector::connectServiceHandler",SOCKTRACE);
00306 
00307     PEER_CONNECTOR& s = *m_sh;
00308     
00309     if ( !s.open (protocol_family_) ) {
00310         EL((ASSA::ASSAERR,"Socket::open (protocol=%d) failed\n",
00311             protocol_family_));
00312         return -1;
00313     }
00314     
00315     m_fd = s.getHandler ();
00316     s.setOption (ASSA::Socket::nonblocking, 1);
00317 
00318     return (s.connect (addr_) ? 0 : -1);
00319 }
00320 
00321 template<class SH, class PC> int
00322 Connector<SH, PC>::
00323 activateServiceHandler ()
00324 {
00325     trace_with_mask("Connector::activateServiceHandler",SOCKTRACE);
00326 
00327     return m_sh->open ();
00328 }
00329 
00330 template<class SH, class PC> void
00331 Connector<SH, PC>::
00332 doAsync (void)
00333 {
00334     trace_with_mask("Connector::doAsync",SOCKTRACE);
00335 
00336     /* We are doing async and 3-way handshake is in
00337      * progress - hook up with Reactor and wait on timer.
00338      * Write event will be our indicator whether connection
00339      * was completed or not.
00340      */
00341     m_reactor->registerIOHandler (this, m_fd, WRITE_EVENT);
00342 
00343     m_tid = m_reactor->registerTimerHandler (this, m_timeout, "ASYNC Connect");
00344     m_state = waiting;
00345 }
00346 
00347 template<class SH, class PC> int
00348 Connector<SH, PC>::
00349 doSync (void)
00350 {
00351     trace_with_mask("Connector::doSync",SOCKTRACE);
00352 
00353     m_reactor = new Reactor;
00354 
00355     m_reactor->registerIOHandler    (this, m_fd, WRITE_EVENT);
00356     m_reactor->registerTimerHandler (this, m_timeout, "SYNC Connect");
00357     m_state = waiting;
00358     m_reactor->waitForEvents (&m_timeout); // Let the ball rolling ...
00359     m_reactor->removeHandler (this);    // Remove all handlers.
00360 
00361     delete m_reactor;
00362     m_reactor = 0;
00363 
00364     if (conned == m_state) 
00365     {
00366         DL((SOCKTRACE,"Synchronous connect() succeeded.\n"));
00367         return 0;
00368     }
00369 
00370     EL((ASSA::ASSAERR,"Synchronous connect() timed out.\n"));
00371     set_errno (ETIMEDOUT);
00372 
00373     return -1;
00374 }
00375 
00376 template<class SH, class PC> int
00377 Connector<SH, PC>::
00378 handle_write (int fd_)
00379 {
00380     trace_with_mask("Connector::handle_write",SOCKTRACE);
00381 
00382     /* Precondition 
00383      */
00384     if (fd_ != m_fd) {
00385         return -1;
00386     }
00387 
00388     /* This method serves both sync and async modes - thus the
00389      * differences. For async we remove Timer here. sync runs
00390      * its own private Reactor and handler termination is 
00391      * handled in doSync().
00392      */
00393 
00394     if (async == m_mode) { // Complete SH activation
00395         m_reactor->removeTimerHandler (m_tid);
00396         m_tid = 0;
00397     }
00398 
00399     /*
00400      * Although SUN and Linux man pages on connect(3) claims that
00401      * "upon asynchronous establishement of connection, select(3)
00402      * will indicate that the file descriptor for the socket is ready
00403      * for writing", as discussed in W.S.Stevens "UNIX network
00404      * programming", Vol I, 2nd edition, BSD-derived systems also
00405      * mark file descriptor both readable and writable when the
00406      * connection establishment encouters an error.
00407      *
00408      * Therefore we need an extra step to find out what really happened.
00409      * One way to do so is to look at socket pending errors...
00410      */
00411 
00412     int error;
00413     int ret;
00414     error = ret = errno = 0;
00415     socklen_t n = sizeof (error);
00416 
00419     m_reactor->removeHandler (this, WRITE_EVENT);   
00420 
00421 #if defined(__CYGWIN32__) 
00422     ret = getsockopt (m_fd, SOL_SOCKET, SO_ERROR, (void*)&error, (int*)&n);
00423 #elif defined (WIN32)
00424     ret = getsockopt (m_fd, SOL_SOCKET, SO_ERROR, (char*)&error, (int*)&n);
00425 #else
00426     ret = getsockopt (m_fd, SOL_SOCKET, SO_ERROR, (void*)&error, &n);
00427 #endif
00428 
00429     if (ret == 0) {
00430         if (error == 0) 
00431         {
00432             if (activateServiceHandler () == 0) {
00433                 DL((SOCKTRACE,"Nonblocking connect() completed\n"));
00434                 m_state = conned;
00435             }
00436             else {
00437                 DL((SOCKTRACE,"Nonblocking connect() failed\n"));
00438                 m_state = failed;
00439             }
00440             return (0);         // return value doesn't really matter
00441         }
00442         /* Socket pending error - propagate it via errno. */
00443 
00444         EL((ASSA::ASSAERR,"Socket pending error: %d\n",error));
00445         set_errno (error);
00446     }
00447     else {  /* Solaris pending error. */
00448         EL((ASSA::ASSAERR,"getsockopt(3) = %d\n", ret));
00449         EL((ASSA::ASSAERR,"Solaris pending error!\n"));
00450     }
00451     m_state = failed;
00452 
00453     EL((ASSA::ASSAERR,"Nonblocking connect (2) failed\n"));
00454 
00455     if (get_errno () == ECONNREFUSED) 
00456     {
00457         EL((ASSA::ASSAERR,"Try to compare port "
00458              "numbers on client and service hosts.\n"));
00459     }
00460         /* This is the only way to tell SH that we failed to connect. 
00461      */
00462     if (async == m_mode) {
00463         m_sh->close (); 
00464     }
00465 
00466     /* Don't alter fd mask - SERVICE_HANDLER::open() could have changed 
00467      * it already for application processing needs.
00468      */
00469     return 0;
00470 }
00471 
00472 template<class SH, class PC> int
00473 Connector<SH, PC>::
00474 handle_timeout (TimerId tid_)
00475 {
00476     trace_with_mask("Connector::handle_timeout",SOCKTRACE);
00477 
00478     m_state = failed;
00479     set_errno (ETIMEDOUT);  // Connection timed out
00480 
00481     if (async == m_mode) {
00482         m_reactor->removeHandler (this, WRITE_EVENT);
00483     }
00484     return -1;      // Remove Timer Handler
00485 }
00486 
00487 } // end namespace ASSA
00488 
00489 #endif /* CONNECTOR_H */  

Generated on Sun Aug 13 15:08:00 2006 for libassa by  doxygen 1.4.6