I l @ ve RuBoard |
MotivationAlthough there are three distinct roles for connection-oriented protocols, the Socket API only supports the following two socket modes:
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 CapabilitiesThe ACE_SOCK_Connector class is a factory that establishes a new end-point of communication actively. It provides the following capabilities:
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:
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:
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. ExampleSidebar 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 |