3.6 The ACE_SOCK_IO and ACE_SOCK_Stream Classes

I l @ ve RuBoard

Motivation

An area of accidental complexity identified in Section 2.3 is the inability to detect socket misuse at compile-time. As described in Section 3.1, connection management involves three roles: active connection role, passive connection role, and communication role. The Socket API, however, defines only two socket modes: data mode and passive mode. Developers can therefore misuse sockets in ways that can't be detected during compilation. The ACE_SOCK_Connector class took the first step toward resolving this area of complexity; the ACE_SOCK_Stream class takes the next step. ACE_SOCK_Stream defines a data-mode "transfer-only" object. An ACE_SOCK_Stream object can't be used in any role other than data transfer without intentionally violating its interface.

Class Capabilities

The ACE_SOCK_Stream class encapsulates the data transfer mechanisms supported by data-mode sockets. This class provides the following capabilities:

  • Support for sending and receiving up to n bytes or exactly n bytes

  • Support for "scatter-read" operations, which populate multiple caller-supplied buffers instead of a single contiguous buffer

  • Support for "gather-write" operations, which transmit the contents of multiple noncontiguous data buffers in a single operation

  • Support for blocking, nonblocking, and timed I/O operations and

  • Support for generic programming techniques [Ale0l] that enable the wholesale replacement of functionality via C++ parameterized types, as described in Sidebar 5 on page 57

ACE_SOCK_Stream instances are initialized by the ACE_SOCK_Acceptor or ACE_SOCK_Connector factories. The interface of the ACE_SOCK_Stream and ACE_SOCK_IO classes are shown in Figure 3.7 on page 61. ACE_SOCK_ Stream derives from ACE_SOCK_IO , which itself derives from ACE_SOCK and defines basic data transfer methods that are reused by the ACE UDP wrapper facades.

Figure 3.7. The ACE_SOCK_Stream and ACE_SOCK_IO Class Diagrams

The key methods exported through ACE_SOCK_Stream are outlined in the following table:

Method Description
 send() recv() 
Transmit and receive buffers of data. They may write or read less than the number of bytes requested due to buffering in the OS and flow control in the transport protocol.
 send_n() recv_n() 
Transmit and receive data buffers of exactly n bytes to simplify application handling of "short- writes " and "short-reads."
recvv_n() Receives multiple buffers of data efficiently and completely using an OS "scatter-read" system function.
sendv_n() Sends multiple buffers of data efficiently and completely using an OS "gather-write" system function.

The ACE_SOCK_Stream class supports blocking, timed, and nonblocking I/O, where blocking I/O is the default. Timed I/O is useful when communicating with peers that can hang or block indefinitely. Two types of ACE_Time_Value values can be passed to the ACE_SOCK_Stream I/O methods to control their time-out behavior:

Value Behavior
NULL ACE_Time_Value pointer Indicates that an I/O method should block until data are transferred or until an error occurs.
A non-NULL ACE Time_ value pointer Indicates that the I/O method should wait a relative amount of time to transfer the data. If the time-out expires before the data is sent or received a -1 is returned with errno set to ETIME.

Nonblocking I/O is useful for applications that can't afford to block when data isn't sent or received immediately. Blocking versus nonblocking I/O can be controlled via the enable() and disable() methods inherited from ACE_IPC_SAP :

 peer.enable (ACE_NONBLOCK); // Enable nonblocking I/O. peer.disable (ACE_NONBLOCK); // Disable nonblocking I/O. 

If an I/O method is invoked on an instance of ACE_SOCK_Stream that's in nonblocking mode and the call would block, a “1 is returned and errno is Set to EWOULDBLOCK.

Example

Now that we've examined the capabilities of ACE_SOCK_Stream , we can show the data transfer portion of our Web client example begun on page 59 in Section 3.5. This code sends an HTTP GET request for a particular URL path name and then prints out the contents of the file that's downloaded from the Web server.

 // ...Connection code from example in Section 3.5 omitted...  char buf [BUFSIZ];  iovec iov[3];  iov[0].iov_base = "GET ";  iov[0].iov_len = 4; // Length of "GET ".  iov[1].iov_base = pathname;  iov[1].iov_len = strlen (pathname);  iov[2] iov_base = " HTTP/1.0\r\n\r\n" ;  iov[2].iov_len = 13; // Length of " HTTP/1.0\r\n\r\n";  if (peer.sendv_n (iov, 3) == -1)    return 1;  for (ssize_t n; (n = peer.recv (buf, sizeof buf)) > 0; )    ACE::write_n (ACE_STDOUT, buf, n);  return peer, close (); } 

We use an array of iovec structures to transmit the HTTP GET request to the Web server efficiently using the ACE_SOCK_Stream: :sendv_n() gather-write method. This avoids performance issues with Nagle's algorithm [Ste93] described in Sidebar 6. On UNIX/POSIX platforms this method is implemented via writev () and on WinSock2 platforms it's implemented via WSASend() .

The I/O methods in the client_download_file() function will block if they encounter TCP flow control or if the Web server misbehaves. To prevent the client from hanging indefinitely, we can add time-outs to these method calls. In the following code, for instance, if the server doesn't receive the data in 10 seconds, a “1 will be returned with errno set to ETIME:

 // Wait no more than 10 seconds to send or receive data. ACE_Time_Value timeout (10); peer.sendv_n (iov, 3, &timeout); while (peer.recv (buf, sizeof buf, &timeout)   >   0)   // ... process the contents of the downloaded file. 

Sidebar 6: Working With ”and Around ”Nagle's Algorithm

By default, most TCP/IP implementations use Nagle's algorithm [Ste93], which buffers small, sequentially sent packets in the sender's TCP/IP stack. Although this algorithm minimizes network congestion, it can increase latency and decrease throughput if you're unaware of how and when it takes effect. These problems can arise when several small buffers are sent in successive one-way operations; for example, the following code will trigger Nagle's algorithm:

 peer.send_n ("GET ", 4); peer.send_n (pathname, strlen (pathname)); peer.send_n (" HTTP/1.0\r\n\r\n", 13); 

Application developers can disable Nagle's algorithm by calling peer.enable() with the TCP_NODELAY flag, which forces TCP to send packets out as soon as possible. The client_download_file() function on page 63 shows an even more efficient solution using sendv_n() to transfer all data buffers in a single system function. This method is passed an array of iovec structures, which are defined as follows :

 struct iovec {   // Pointer to a buffer.   char *iov_base;   // Length of buffer pointed to by <iov_base>.   int iov_len; }; 

Some OS platforms define iovec natively, whereas in other platforms it's defined by ACE. Member names are the same in all cases, but they're not always in the same order, so set them explicitly rather than via struct initialization.

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