libassa 3.5.0
|
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 */