libassa  3.5.1
Connector.h
Go to the documentation of this file.
1 // -*- c++ -*-
2 //------------------------------------------------------------------------------
3 // Connector.h
4 //------------------------------------------------------------------------------
5 // Copyright (C) 1999 Vladislav Grinchenko
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Library General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
11 //------------------------------------------------------------------------------
12 #ifndef CONNECTOR_H
13 #define CONNECTOR_H
14 
15 #include <unistd.h> // fcntl(2)
16 #include <fcntl.h> // fcntl(2)
17 
18 #if defined(WIN32)
19 typedef unsigned int socklen_t;
20 #else
21 # include <sys/socket.h>
22 #endif
23 
24 #include <string.h> // strerror(3)
25 #include <errno.h> // errno(3)
26 
27 #include "assa/Logger.h"
28 #include "assa/EventHandler.h"
29 #include "assa/Reactor.h"
30 #include "assa/TimeVal.h"
31 #include "assa/Address.h"
32 #include "assa/Socket.h"
33 
34 namespace ASSA {
35 
43 enum ConnectMode {
44  sync,
45  async
46 };
47 
62 template<class SERVICE_HANDLER, class PEER_CONNECTOR>
63 class Connector : public virtual EventHandler
64 {
65 public:
67  Connector ();
68 
70  virtual ~Connector ();
71 
83  virtual int open (const TimeVal& tv_ = TimeVal (5.0),
84  ConnectMode mode_ = sync,
85  Reactor* r_ = (Reactor*)NULL);
86 
91  virtual int close (void);
92 
117  virtual int connect (SERVICE_HANDLER* sh_,
118  Address& addr_,
119  int protocol_ = AF_INET);
120 
122  virtual int handle_write (int fd);
123 
125  virtual int handle_timeout (TimerId tid);
126 
127 protected:
135  failed
136  };
137 
145  virtual SERVICE_HANDLER* makeServiceHandler (SERVICE_HANDLER* sh_);
146 
152  virtual int connectServiceHandler (Address& addr, int protocol);
153 
157  virtual int activateServiceHandler ();
158 
159 protected:
162 
165 
168 
171 
173  int m_flags;
174 
176  SERVICE_HANDLER* m_sh;
177 
179  int m_fd;
180 
183 
184 private:
186  void doAsync (void);
187 
191  int doSync (void);
192 };
193 
194 // Convenience definitions
195 
196 #define SH SERVICE_HANDLER
197 #define PC PEER_CONNECTOR
198 
199 //------------------------------------------------------------------------------
200 // Template member functions definitions
201 //------------------------------------------------------------------------------
202 
203 template<class SH, class PC>
205 Connector ()
206  : m_tid (0), m_reactor (0), m_state (idle),
207  m_flags (0), m_sh ((SERVICE_HANDLER*)NULL), m_fd (-1), m_mode (sync)
208 {
209  trace_with_mask("Connector::Connector",SOCKTRACE);
210  set_id ("Connector");
211 }
212 
213 template<class SH, class PC>
215 ~Connector ()
216 {
217  trace_with_mask("Connector::~Connector",SOCKTRACE);
218  // If I created SERVICE_HANDLER, should I delete it too?
219 }
220 
221 template<class SH, class PC> int
223 open (const TimeVal& tv_, ConnectMode mode_, Reactor* r_)
224 {
225  trace_with_mask("Connector::open", SOCKTRACE);
226 
227  m_timeout = tv_;
228  if (async == mode_ && (Reactor*) NULL == r_)
229  return -1;
230  m_mode = mode_;
231  m_reactor = r_;
232  return 0;
233 }
234 
235 template<class SH, class PC> int
237 close ()
238 {
239  trace_with_mask("Connector::close",SOCKTRACE);
240  return 0;
241 }
242 
243 template<class SH, class PC> int
245 connect (SH* sh_, Address& addr_, int protocol_family_)
246 {
247  /*
248  * We restore socket to its original mode only on
249  * successful connection. If error occured, client would have
250  * to close socket anyway.
251  *
252  * NOTE: If sh_==0, then result is dangling pointer
253  * new_sh produced ! Destructor should determine whether
254  * SERVICE_HANDLER has been created dynamically and if so, delete
255  * it.
256  */
257  trace_with_mask("Connector::connect",SOCKTRACE);
258  errno = 0;
259 
260  m_sh = makeServiceHandler (sh_);
261  PEER_CONNECTOR& s = *m_sh;
262 
263  if (addr_.bad ()) {
264  set_errno (EFAULT); // Bad address
265  EL((ASSA::ASSAERR,"Bad address (errno %d)\n", errno));
266  return -1;
267  }
268 
269  if (connectServiceHandler (addr_, protocol_family_) == -1)
270  {
271  int e = get_errno ();
272  if (e == EINPROGRESS || e == EWOULDBLOCK)
273  {
274  if (async == m_mode) {
275  doAsync ();
276  return 0;
277  }
278 
279  return doSync ();
280  }
281  return -1;
282  }
283 
284  return activateServiceHandler ();
285 }
286 
287 template<class SH, class PC> SERVICE_HANDLER*
289 makeServiceHandler (SERVICE_HANDLER* sh_)
290 {
291  trace_with_mask("Connector::makeServiceHandler",SOCKTRACE);
292 
293  SERVICE_HANDLER* new_sh = sh_;
294 
295  if (sh_ == 0) {
296  new_sh = new SERVICE_HANDLER;
297  }
298  return new_sh;
299 }
300 
301 template<class SH, class PC> int
303 connectServiceHandler (Address& addr_, int protocol_family_)
304 {
305  trace_with_mask("Connector::connectServiceHandler",SOCKTRACE);
306 
307  PEER_CONNECTOR& s = *m_sh;
308 
309  if ( !s.open (protocol_family_) ) {
310  EL((ASSA::ASSAERR,"Socket::open (protocol=%d) failed\n",
311  protocol_family_));
312  return -1;
313  }
314 
315  m_fd = s.getHandler ();
316  s.setOption (ASSA::Socket::nonblocking, 1);
317 
318  return (s.connect (addr_) ? 0 : -1);
319 }
320 
321 template<class SH, class PC> int
324 {
325  trace_with_mask("Connector::activateServiceHandler",SOCKTRACE);
326 
327  return m_sh->open ();
328 }
329 
330 template<class SH, class PC> void
332 doAsync (void)
333 {
334  trace_with_mask("Connector::doAsync",SOCKTRACE);
335 
336  /* We are doing async and 3-way handshake is in
337  * progress - hook up with Reactor and wait on timer.
338  * Write event will be our indicator whether connection
339  * was completed or not.
340  */
341  m_reactor->registerIOHandler (this, m_fd, WRITE_EVENT);
342 
343  m_tid = m_reactor->registerTimerHandler (this, m_timeout, "ASYNC Connect");
344  m_state = waiting;
345 }
346 
347 template<class SH, class PC> int
349 doSync (void)
350 {
351  trace_with_mask("Connector::doSync",SOCKTRACE);
352 
353  m_reactor = new Reactor;
354 
355  m_reactor->registerIOHandler (this, m_fd, WRITE_EVENT);
356  m_reactor->registerTimerHandler (this, m_timeout, "SYNC Connect");
357  m_state = waiting;
358  m_reactor->waitForEvents (&m_timeout); // Let the ball rolling ...
359  m_reactor->removeHandler (this); // Remove all handlers.
360 
361  delete m_reactor;
362  m_reactor = 0;
363 
364  if (conned == m_state)
365  {
366  DL((SOCKTRACE,"Synchronous connect() succeeded.\n"));
367  return 0;
368  }
369 
370  EL((ASSA::ASSAERR,"Synchronous connect() timed out.\n"));
371  set_errno (ETIMEDOUT);
372 
373  return -1;
374 }
375 
376 template<class SH, class PC> int
378 handle_write (int fd_)
379 {
380  trace_with_mask("Connector::handle_write",SOCKTRACE);
381 
382  /* Precondition
383  */
384  if (fd_ != m_fd) {
385  return -1;
386  }
387 
388  /* This method serves both sync and async modes - thus the
389  * differences. For async we remove Timer here. sync runs
390  * its own private Reactor and handler termination is
391  * handled in doSync().
392  */
393 
394  if (async == m_mode) { // Complete SH activation
395  m_reactor->removeTimerHandler (m_tid);
396  m_tid = 0;
397  }
398 
399  /*
400  * Although SUN and Linux man pages on connect(3) claims that
401  * "upon asynchronous establishement of connection, select(3)
402  * will indicate that the file descriptor for the socket is ready
403  * for writing", as discussed in W.S.Stevens "UNIX network
404  * programming", Vol I, 2nd edition, BSD-derived systems also
405  * mark file descriptor both readable and writable when the
406  * connection establishment encouters an error.
407  *
408  * Therefore we need an extra step to find out what really happened.
409  * One way to do so is to look at socket pending errors...
410  */
411 
412  int error;
413  int ret;
414  error = ret = errno = 0;
415  socklen_t n = sizeof (error);
416 
419  m_reactor->removeHandler (this, WRITE_EVENT);
420 
421 #if defined(__CYGWIN32__)
422  ret = getsockopt (m_fd, SOL_SOCKET, SO_ERROR, (void*)&error, (int*)&n);
423 #elif defined (WIN32)
424  ret = getsockopt (m_fd, SOL_SOCKET, SO_ERROR, (char*)&error, (int*)&n);
425 #else
426  ret = getsockopt (m_fd, SOL_SOCKET, SO_ERROR, (void*)&error, &n);
427 #endif
428 
429  if (ret == 0) {
430  if (error == 0)
431  {
432  if (activateServiceHandler () == 0) {
433  DL((SOCKTRACE,"Nonblocking connect() completed\n"));
434  m_state = conned;
435  }
436  else {
437  DL((SOCKTRACE,"Nonblocking connect() failed\n"));
438  m_state = failed;
439  }
440  return (0); // return value doesn't really matter
441  }
442  /* Socket pending error - propagate it via errno. */
443 
444  EL((ASSA::ASSAERR,"Socket pending error: %d\n",error));
445  set_errno (error);
446  }
447  else { /* Solaris pending error. */
448  EL((ASSA::ASSAERR,"getsockopt(3) = %d\n", ret));
449  EL((ASSA::ASSAERR,"Solaris pending error!\n"));
450  }
451  m_state = failed;
452 
453  EL((ASSA::ASSAERR,"Nonblocking connect (2) failed\n"));
454 
455  if (get_errno () == ECONNREFUSED)
456  {
457  EL((ASSA::ASSAERR,"Try to compare port "
458  "numbers on client and service hosts.\n"));
459  }
460  /* This is the only way to tell SH that we failed to connect.
461  */
462  if (async == m_mode) {
463  m_sh->close ();
464  }
465 
466  /* Don't alter fd mask - SERVICE_HANDLER::open() could have changed
467  * it already for application processing needs.
468  */
469  return 0;
470 }
471 
472 template<class SH, class PC> int
474 handle_timeout (TimerId tid_)
475 {
476  trace_with_mask("Connector::handle_timeout",SOCKTRACE);
477 
478  m_state = failed;
479  set_errno (ETIMEDOUT); // Connection timed out
480 
481  if (async == m_mode) {
482  m_reactor->removeHandler (this, WRITE_EVENT);
483  }
484  return -1; // Remove Timer Handler
485 }
486 
487 } // end namespace ASSA
488 
489 #endif /* CONNECTOR_H */
Address is an abstraction for INET or UNIX-domain address data type.
#define SH
Definition: Connector.h:196
An abstract interface for handling I/O events, timers, and such.
An abstraction to message logging facility.
#define EL(X)
A macro for writing error message to the Logger.
Definition: Logger.h:285
#define DL(X)
A macro for writing debug message to the Logger.
Definition: Logger.h:273
#define trace_with_mask(s, m)
trace_with_mask() is used to trace function call chain in C++ program.
Definition: Logger.h:437
An implementation of Reactor pattern.
Abstraction of socket data type.
Class TimeVal is a wrapper around UNIX timeval structure.
bool bad() const
Indicates whether there was error during address construction process i.e.
Definition: Address.h:80
Connector is a template class for initialization of communication services.
Definition: Connector.h:64
void doAsync(void)
Setup for asynchronous mode completion.
Definition: Connector.h:332
virtual int close(void)
Do-nothing close.
Definition: Connector.h:237
virtual int connectServiceHandler(Address &addr, int protocol)
Default strategy is to make synchronous connection with no timeouts.
Definition: Connector.h:303
int m_flags
Socket flags (obsolete)
Definition: Connector.h:173
ProgressState
state.
Definition: Connector.h:131
@ idle
Initialized.
Definition: Connector.h:132
@ failed
Failed to connect.
Definition: Connector.h:135
@ conned
Connected.
Definition: Connector.h:134
@ waiting
Asynchronously waiting on connection completion.
Definition: Connector.h:133
TimeVal m_timeout
Timeout.
Definition: Connector.h:161
virtual int activateServiceHandler()
Activate handler by calling its open() method.
Definition: Connector.h:323
virtual int open(const TimeVal &tv_=TimeVal(5.0), ConnectMode mode_=sync, Reactor *r_=(Reactor *) NULL)
Configure Connector.
Definition: Connector.h:223
virtual int connect(SERVICE_HANDLER *sh_, Address &addr_, int protocol_=AF_INET)
Define strategy for establishing connection.
Definition: Connector.h:245
virtual int handle_write(int fd)
Handle connection completion.
Definition: Connector.h:378
virtual ~Connector()
Destructor. Do-nothing.
Definition: Connector.h:215
virtual SERVICE_HANDLER * makeServiceHandler(SERVICE_HANDLER *sh_)
Defines creation strategy for ServiceHandler.
Definition: Connector.h:289
Connector()
Constructor. Do-nothing.
Definition: Connector.h:205
ConnectMode m_mode
Mode (sync/async)
Definition: Connector.h:182
ProgressState m_state
Connection progress state.
Definition: Connector.h:170
SERVICE_HANDLER * m_sh
Reference to ServiceHandler.
Definition: Connector.h:176
virtual int handle_timeout(TimerId tid)
Handler connection timeout.
Definition: Connector.h:474
TimerId m_tid
Timer id.
Definition: Connector.h:164
int doSync(void)
Synchronous mode completion.
Definition: Connector.h:349
int m_fd
Socket file descriptor.
Definition: Connector.h:179
Reactor * m_reactor
Reference to Reactor (for async)
Definition: Connector.h:167
EventHandler class.
Definition: EventHandler.h:103
void set_id(const std::string &id_)
Set EventHandler ID.
Definition: EventHandler.h:153
bool registerIOHandler(EventHandler *eh_, handler_t fd_, EventType et_=RWE_EVENTS)
Register I/O Event handler with Reactor.
Definition: Reactor.cpp:93
@ nonblocking
Set Socket to a non-blocking mode (O_RDWR|O_NONBLOCK).
Definition: Socket.h:115
Definition: Acceptor.h:40
@ 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
void set_errno(int new_errno_)
Set error number in a portable way.
Definition: Logger_Impl.h:128
unsigned long TimerId
Timer Id is used in handle_timeout() calls.
Definition: EventHandler.h:27
@ SOCKTRACE
Extended Socket & friends messages
Definition: LogMask.h:42
@ ASSAERR
ASSA and system errors
Definition: LogMask.h:34
int get_errno()
Fetch error number in a portable way.
Definition: Logger_Impl.h:115
ConnectMode
Definition: Connector.h:43
@ sync
Synchronous connection mode.
Definition: Connector.h:44
@ async
Asynchronous connection mode.
Definition: Connector.h:45