3.5 The ACE_SOCK_Connector Class

I l @ ve RuBoard

Motivation

Although there are three distinct roles for connection-oriented protocols, the Socket API only supports the following two socket modes:

  1. A data-mode socket is used by peer applications in their communication roles to exchange data between connected peers and

  2. A passive-mode socket is a factory used by a peer application in its passive connection role to return a handle to a connected data-mode socket.

No socket mode focuses exclusively on the active connection role, however. Instead, a data-mode socket also plays this role by convention. An application is therefore not supposed to call send() or recv() on a data-mode socket until after the connect() function establishes a connection successfully.

The asymmetry in the Socket API between the connection roles and the socket modes is confusing and error-prone . For example, an application may accidentally call recv() or send() on a data-mode socket handle before it's connected. Unfortunately, this problem can't be detected until run time since socket handles are weakly -typed and thus the dual role of data-mode sockets is only enforced via programming convention, rather than via the compiler's type-checking features. ACE therefore defines the ACE_SOCK_Connector class, which prevents accidental misuse by only exposing methods for actively establishing connections.

Class Capabilities

The ACE_SOCK_Connector class is a factory that establishes a new end-point of communication actively. It provides the following capabilities:

  • It initiates a connection with a peer acceptor and then initializes an ACE_SOCK_Stream object after the connection is established.

  • Connections can be initiated in either a blocking, nonblocking, or timed manner and

  • C++ traits are used to support generic programming techniques that enable the wholesale replacement of functionality via C++ parameterized types, as described in Sidebar 5 on page 57.

Sidebar 5: Using Traits for ACE Socket Wrapper Facades

To simplify the wholesale replacement of IPC classes and their associated addressing classes, the ACE Socket wrapper facades define traits . Traits are a C++ generic programming [Ale01] idiom that can be used to define and combine a set of characteristics to alter the behavior(s) of a template class [Jos99]. The ACE Socket wrapper facades use traits to define the following class associations:

  • PEER _ADDR ” This trait defines the ACE_INET_Addr addressing class that's associated with ACE Socket wrapper facade classes and

  • PEER _STREAM ” This trait defines the ACE_SOCK_Stream data transfer class that's associated with the ACE_SOCK_Acceptor and ACE_ SOCK_Connector factories.

ACE implements these traits as C++ type definitions, which are shown as notes in the UML figures in this chapter. Section A.5.3, page 253, illustrates how to use these traits to write concise generic functions and classes. We explore other uses of ACE traits and traits classes in (SH).

The interface of ACE_SOCK_Connector is shown in Figure 3.6. The two key methods in ACE_SOCK_Connector are outlined in the following table:

Method Description
connect() Actively connects an ACE_SOCK_Stream at a particular network address using either blocking, nonblocking, or timed mode.
complete() Tries to complete a nonblocking connection and initialize an ACE_SOCK_Stream .
Figure 3.6. The ACE_SOCK_Connector Class Diagram

ACE_SOCK_Connector supports blocking, nonblocking, and timed connections, where blocking is the default. Nonblocking and timed connections are useful when establishing connections over high-latency links, using single-threaded applications, or initializing many peers that can be connected in an arbitrary order. Three types of ACE_Time_Value values can be passed to the connect() method to control its behavior:

Value Behavior
NULL ACE_Time_Value pointer Indicates connect() should wait indefinitely, that is, block until the connection is established or the OS deems the server host unreachable.
Non-NULL ACE_Time_Value pointer whose sec() and usec() methods return 0 Indicates connect() should perform a non-blocking connection, that is, if the connection isn't established immediately, return “1 and set errno to EWOULDBLOCK.
A non-NULL ACE Time_Value pointer whose sec() or usec() method returns > 0 Indicates connect() should only wait a relative amount of time to establish the connection, returning “1 with errno set to ETIME if it can't establish the connection by then.

The ACE_SOCK_Connector 's connection time-out support is particularly useful in practice since the ways in which Socket APIs implement connection time-outs differs widely across OS platforms.

Since the underlying socket API doesn't use a factory socket to connect data-mode sockets, the ACE_SOCK_Connector class needn't inherit from the ACE_SOCK class described on page 54 in Section 3.4. It therefore doesn't have its own socket handle. Instead, an ACE_SOCK_Connector borrows the handle from the ACE_SOCK_Stream passed to its connect() method and uses it to establish the connection actively. As a result, instances of ACE_SOCK_Connector don't store any state, so they can be used reentrantly in multithreaded programs without the need for additional locks.

Example

Sidebar 2 on page 19 describes how to build the ACE library so that you can experiment with the examples we show in the book. Our first example illustrates how the ACE_SOCK_Connector can be used to connect a client application to a Web server. We start by including the necessary ACE Socket wrapper header files:

 #include "ace/INET_Addr.h" #include "ace/SOCK_Connector.h" #include "ace/SOCK_Stream.h" 

We then define the main() function, which establishes a connection with a Web server listening at port 80. This is the standard port number used by Web servers that support the Hypertext Transport Protocol (HTTP) [Ste96]. HTTP is a simple protocol layered on top of TCP and used by clients to download content from a Web server.

 int main (int argc, char *argv[]) {   const char *pathname =     argc > 1 ? argv[1] : "index.html";   const char *server_hostname =     argc > 2 ? argv[2] . "ace.ece.uci.edu";   ACE_SOCK_Connector  connector;   ACE_SOCK_Stream peer;   ACE _INET_Addr peer_addr;   if (peer_addr.set (80, server_hostname) == -1)     return 1;   else if (connector.connect (peer, peer_addr) == -1)     return 1;   // ... 

We complete the rest of this example on page 63 in Section 3.5 after first describing the capabilities of the ACE_SOCK_Stream data transfer class.

Note that the connector.connect() call above is synchronous; that is, it will block until either the connection is established or connect request fails. As shown in the table on page 58, the ACE Socket wrapper facades make it easy to perform nonblocking or timed connections portably. For example, the following code illustrates how to modify our client application to perform a nonblocking connect() to a Web server:

 // Designate a nonblocking connect. if (connector.connect (peer,                        peer_addr,                        &ACE_Time_Value::zero) == -1) {   if (errno == EWOULDBLOCK) {     // Do some other work ...     // Now, try to complete the connection establishment,     // but don't block if it isn't complete yet.     if (connector.complete (peer,                             0,                             &ACE_Time_Value::zero)   ==   -1)       // ... 

Likewise, a timed connect() can be performed as follows :

 ACE_Time_Value timeout (10); // Set time-out to 10 seconds if (connector.connect (peer, peer, addr, &timeout) == -1) {   if (errno == ETIME)     // Time-out, do something else... } 
I l @ ve RuBoard


C++ Network Programming
C++ Network Programming, Volume I: Mastering Complexity with ACE and Patterns
ISBN: 0201604647
EAN: 2147483647
Year: 2001
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net