You are on page 1of 14

ACE C++ Wrapper Tutorial

Problem: Software Evolution


Developing Ecient and Portable DX  Key Challenges
Communication Software with ACE and C++
IMAGE
STORE
{ Communication software
evolves over time
Requirements change
ATM

Chris Gill and Douglas C. Schmidt
LAN

Platforms change
DIAGNOSTIC
STATIONS 
New design forces emerge
Distributed Object Computing Group ATM { It is essential to plan for
Computer Science Department, Washington University, St. Louis MAN
CLUSTER inevitable change
ATM IMAGE
LAN

cdgill@cs.wustl.edu
STORE

http://www.cs.wustl.edu/schmidt/ACE-examples4.ps.gz
MODALITIES CENTRAL
(C T , M R , C R ) IMAGE
STORE

ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial


Solution: Plan for Change Using Frameworks and Sources of Variation in Communication Software
Patterns
 Syntactic Variations
 Solution Approach SATELLITES
TRACKING
STATION
{ Unsupported non-essential APIs
Reactor/Proactor Strategy Singleton
PEERS

{ Identify sources of
Memento

State

I/O Strategy Cached Virtual


Framework Filesystem
{ Gratuitous di erences in API
commonality and
Asynchronous Completion Token Tilde ~ variability STATUS INFO  Semantic Variations
Service Configurator

Protocol
Expander
/home/... { Use patterns to WIDE AREA
NETWORK
{ Underlying platform di erences
identify reusable
Acceptor

Handler Event Dispatcher

{ Framework must respect these


COMMANDS BULK DATA

design artifacts
Protocol TRANSFER
Filter

{ Use frameworks to GATEWAY di erences


\unify" variation in Complex Variations
Service Configurator

Adapter

code artifacts
LOCAL AREA NETWORK

{ Unsupported essential portions


Concurrency
State

Protocol Pipeline Strategy GROUND


STATION
of API
Framework Framework
Pipes and Filters Active Object Strategy PEERS

{ Emulation is necessary
2 3
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
ACE framework: Resolving Syntactic Variations ACE framework: Resolving Semantic Variations
int ACE_OS::fstat (ACE_HANDLE handle,  Examples int ACE_OS::clock_gettime  Examples
struct stat *stp) (clockid_t clockid, struct timespec *ts)

{ { Unsupported { { Underlying di erences


#if defined (ACE_PSOS_LACKS_PHILE)  Provide \no-op" #if defined (ACE_HAS_CLOCK_GETTIME)
ACE_OSCALL_RETURN (::clock_gettime
Time in clock ticks
ACE_UNUSED_ARG (handle); de nitions (clockid, ts), int, -1); Ticks-per-second is
ACE_UNUSED_ARG (stp);  Conditional compilation #elif defined (ACE_PSOS) board-dependent
ACE_NOTSUP_RETURN (-1); { Syntax ACE_UNUSED_ARG (clockid);
ACE_PSOS_Time_t pt; { Framework must respect
#elif defined (ACE_PSOS)
 Re-map function int result = ACE_PSOS_Time_t::get_system_time (pt); these di erences
ACE_OSCALL_RETURN
(::fstat_f (handle, stp), int, -1);
parameters *ts = ACE_static_cast (struct timespec, pt);
return result;
 Provide a consistent
#else #else abstraction
ACE_OSCALL_RETURN
ACE_UNUSED_ARG (clockid);  Intermediate wrappers
(::fstat (handle, stp), int, -1);
ACE_UNUSED_ARG (ts);
ACE_NOTSUP_RETURN (-1);
are useful for small,
#endif /* ACE_PSOS_LACKS_PHILE */ #endif /* ACE_HAS_CLOCK_GETTIME */ coherent abstractions
} }

4 5

ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial


ACE framework: Resolving Complex Variations Network Programming Alternatives
void *ACE_TSS_Emulation::tss_open  Examples
(void *ts_storage[ACE_TSS_KEYS_MAX]) USER  Communication
{ { Unsupported but essential HI
DOC MIDDLEWARE SPACE software can be
#if defined (ACE_PSOS) portions of the API (e.g., programmed at
thread-speci c storage) SOCKETS AND TLI

ABSTRACTION
u_long tss_base;
several levels of
LEVEL OF
tss_base = (u_long) ts_storage;  Provided by POSIX, NT OPEN/CLOSE/PUTMSG/GETMSG abstraction
t_setreg (0, PSOS_TASK_REG_TSS, tss_base);  Not provided by
VxWorks, pSOS  Di erent levels
void **tss_base_p = ts_storage;
STREAMS TPI are appropriate
for (u_int i = 0;  Emulation in user space is NPI KERNEL for di erent tasks
i < ACE_TSS_KEYS_MAX; necessary LO FRAMEWORK DLPI SPACE
++i, ++tss_base_p)
*tss_base_p = 0; { Create a TSS emulation
return (void *) tss_base; class
#elif defined (...)
{ Provide platform-speci c
// ...
method implementations
6 7
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
Navigating Through the Design Alternatives Overview of DOC Middleware
Choosing the appropriate level of abstaction to program involves many IDL
IDL  Helps simplify
factors
INTERFACE
INTERFACE IMPLEMENTATION
IMPLEMENTATION
REPOSITORY
REPOSITORY COMPILER
COMPILER REPOSITORY
REPOSITORY many types of
 Performance in args applications
operation() OBJECT
CLIENT
{ Higher levels may be less ecient out args + return value
(SERVANT)  Lets developers
work at higher
 Functionality
IDL
DSI
levels of
{ Certain features, e.g., multicast, are not available at all levels DII
IDL
STUBS
ORB
SKELETON
OBJECT abstraction
INTERFACE ADAPTER
 Ease of programming  Examples include
{ DOC middleware is typically easier to use GIOP/IIOP ORB CORE CORBA, DCOM,
Java RMI, DCE,
 Portability Sun RPC
STANDARD INTERFACE STANDARD LANGUAGE MAPPING

ORB-SPECIFIC INTERFACE STANDARD PROTOCOL


{ The socket API is generally portable... www.cs.wustl.edu/schmidt/corba.html
8 9

ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial


Common DOC Middleware Features DOC Middleware Limitations
 DOC middleware \stub/skeleton compiler" support  Some applications may need to access lower-level IPC mechanisms
{ Automatically generate code to perform presentation layer conversions directly to meet certain requirements
 e.g., network byte-ordering and parameter marshaling { e.g., performance, functionality, portability, etc.
 DOC middleware runtime support  Compared with direct use of sockets and TLI, DOC middleware may be
{ Handle network addressing and remote service identi cation less ecient due to
{ Perform service registration, port monitoring, and service dispatching { Presentation conversion processing and excessive data copying
{ Enforce authentication and security { Synchronous client-side and server-side stub behavior
{ Manage transport protocol selection and request delivery { Stop-and-wait ow control
{ Provide reliable operation delivery { Non-adaptive retransmission timer schemes
{ Demultiplexing and dispatching { Non-optimized demultiplexing and concurrency models
{ Concurrency and connection management

10 11
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
Standard APIs for Network IPC Socket Taxonomy
 Sockets and TLI allow ON
COMMUNICATION DOMAIN
 The Socket
APPLICATION
DISTRIBUTED
PROCESS 3592
APPLICATION 1
APPLICATION
DISTRIBUTED
PROCESS4183
APPLICATION 2
APPLICATION
DISTRIBUTED
PROCESS 8729
APPLICATION 3
access to lower-level N EC
TI
LE PAS
SIV
E LOCAL LOCAL/REMOTE
API can be
IPC mechanisms, e.g.:
N
CO RO
E
classi ed
USER { TCP/IP AC
TIV
along three
dimensions

TYPE OF COMMUNICATION SERVICE


SPACE { XNS and Novell socket(PF_UNX)
bind() recvfrom()
socket(PF_INET)
bind() recvfrom()

GRAM
SSYSTEM
YSTEM V

DATA
SOCKET
BSD
API
SOCKET
TLI API
V
IPX NetWare socket(PF_UNIX) socket(PF_INET)
protocols
API TLI API bind() sendto() bind() sendto()

{ UNIX domain

CONNECTED
STREAM DATAGRAM
socket(PF_UNIX) socket(PF_INET)
OS KERNEL KERNEL
sockets
bind() connect() recv() bind() connect() recv()
PROTOCOL SERVICES SPACE socket(PF_UNIX) socket(PF_INET)
(TCP/IP, OSI, ETC.) { OSI protocols bind() connect() send()
accept(PF_UNIX)
bind() connect() send()
accept(PF_INET)
listen() send()/recv() listen() send()/recv()
socket(PF_UNIX) bind() socket(PF_INET) bind()
NETWORK connect() send()/recv() connect() send()/recv()
INTERFACE

12 13

ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial


Problem with Sockets: Lack of Type-safety Problem with Sockets: Steep Learning Curve
int buggy_echo_server (u_short port_num)  I/O handles are Many socket/TLI API functions have complex semantics, e.g.:
{ // Error checking omitted. not amenable to
sockaddr_in s_addr;
strong type
int s_fd = socket (PF_UNIX, SOCK_DGRAM, 0);
s_addr.sin_family = AF_INET; checking at  Multiple protocol families and address families
s_addr.sin_port = port_num; compile-time { e.g., TCP, UNIX domain, OSI, XNS, etc.
s_addr.sin_addr.s_addr = INADDR_ANY;
 The adjacent
bind (s_fd, (sockaddr *) &s_addr,
code contains  Infrequently used features, e.g.:
sizeof s_addr);
int n_fd = accept (s_fd, 0, 0); many subtle, { Broadcasting/multicasting
for (;;) { common bugs { Passing open le handles
char buf[BUFSIZ];
ssize_t n = read (s_fd, buf, sizeof buf); { Urgent data delivery and reception
if (n <= 0) break; { Asynch I/O, non-blocking I/O, I/O-based and timer-based event
}
write (n_fd, buf, n); multiplexing
}

14 15
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
Problem with Sockets: Poorly Structured Problem with Sockets: Portability
Having multiple \standards," i.e., sockets and TLI, makes portability
gethostbyname()
getservbyname()


dicult, e.g.,
getsockname()
getpeername()
getsockopt()
setsockopt()

{ May require conditional compilation


recvfrom()

sendmsg()
recvmsg()
connect()

{ In addition, related functions are not included in POSIX standards


sendto()
socket()

accept()

writev()
readv()
listen()

write()

send()
read()

recv()
bind()

 e.g., select, WaitForMultipleObjects, and poll


 Portability between UNIX and Win32 Sockets is problematic, e.g.:
{ Header les
 Note the socket API is linear rather than hierarchical { Error numbers
{ Thus, it gives no hints on how to use it correctly { Handle vs. descriptor types
{ Shutdown semantics
 In addition, there is no consistency among names... { I/O controls and socket options
16 17
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
The ACE C++ IPC Wrapper Solution Intent and Structure of the Wrapper Facade Pattern
A
:Wrapper  Intent
IPC_SAP
client 1: request ()
request() { Encapsulates low-level,
SOCK_SAP TLI_SAP SPIPE_SAP FIFO_SAP
stand-alone system
mechanisms within type-safe,
modular, and portable class
2: specific_request() interfaces
TRANSPORT
SOCKET STREAM PIPE NAMED PIPE
API
LAYER
INTERFACE API
API API  Forces Resolved
:Wrappee
{ Avoid tedious, error-prone,
 ACE provides C++ \wrappers" that encapsulate IPC programming specific_request() and non-portable system
interfaces like sockets and TLI APIs
{ Create cohesive abstractions
{ This is an example of the Wrapper Facade Pattern
18 19
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
The ACE C++ Socket Wrapper Class Structure SOCK SAP Factory Class Interfaces
COMMUNICATION DOMAIN  Note how class SOCK_Connector class SOCK_Acceptor
N
IO LOCAL LOCAL/REMOTE { : public SOCK
CT E stand-alone
N NE E IV public: {
L SS
CO RO PA functions are
E // Traits public:
TIV replaced by typedef INET_Addr PEER_ADDR; // Traits
AC
C++ class typedef SOCK_Stream PEER_STREAM; typedef INET_Addr PEER_ADDR;
LSOCK_Dgram SOCK_Dgram typedef SOCK_Stream PEER_STREAM;
components int connect
SOCK_Dgram

DATA
GRAM
LSOCK_Dgram SOCK_Dgram_Bcast (SOCK_Stream &new_sap, SOCK_Acceptor
SOCK_Dgram_Mcast
const INET_Addr &raddr, (const INET_Addr &local_addr);
LSOCK_Dgram SOCK_Dgram
Time_Value *timeout, int accept
const INET_Addr &laddr); (SOCK_Stream &new_sap,
LSOCK_CODgram SOCK_CODgram // ... INET_Addr *,

CONNECTED
LSOCK_Acceptor SOCK_Acceptor
}; Time_Value *);
LSOCK_Stream SOCK_Stream //...
LOCK_Connector SOCK_Connector };

STREAM DATAGRAM
LSOCK_Stream SOCK_Stream

TYPE OF COMMUNICATION SERVICE


20 21

ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial


SOCK SAP Stream and Addressing Class Interfaces OO Design Interlude
class SOCK_Stream : public SOCK class INET_Addr : public Addr Q: Why decouple the SOCK Acceptor and the SOCK Connector from
{ { SOCK Stream?
public: public:
// Trait. INET_Addr (u_short port_number, A: For the same reasons that Acceptor and Connector are decoupled
typedef INET_Addr PEER_ADDR; const char host[]);
u_short get_port_number (void);
from Svc Handler, e.g.,
ssize_t send (const void *buf, int32 get_ip_addr (void);
int n); // ...  A SOCK Stream is only responsible for data transfer
ssize_t recv (void *buf, };
int n); { Regardless of whether the connection is established passively or
ssize_t send_n (const void *buf,
int n);
actively
ssize_t recv_n (void *buf,
 This ensures that the SOCK* components are not used incorrectly...
int n);
int close (void);
// ...
{ e.g., you can't accidentally read or write on SOCK Connectors or
}; SOCK Acceptors, etc.

22 23
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
ACE C++ Wrapper echo server A Generic Version of the Echo Server
int echo_server (u_short port_num) template <class ACCEPTOR>
{ int echo_server (u_short port)
// Error handling omitted. {
INET_Addr my_addr (port_num); // Local address of server (note use of traits).
SOCK_Acceptor acceptor (my_addr); ACCEPTOR::PEER_ADDR my_addr (port);
SOCK_Stream new_stream; // Initialize the passive mode server.
ACCEPTOR acceptor (my_addr);
acceptor.accept (new_stream); // Data transfer object (note use of traits).
ACCEPTOR::PEER_STREAM stream;
// Accept a new connection.
for (;;)
acceptor.accept (stream);
{
char buf[BUFSIZ];
for (;;) {
// Error caught at compile time! char buf[BUFSIZ];
ssize_t n = acceptor.recv (buf, sizeof buf); ssize_t n = stream.recv (buf, sizeof buf);
new_stream.send_n (buf, n); stream.send_n (buf, n);
} }
} }

24 25

ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial


Socket vs. ACE C++ Socket Wrapper Example Network Pipe with Sockets
2: ACTIVE
 The following slides illustrate di erences between using the Socket ROLE
1: PASSIVE
interface vs. the ACE C++ Socket wrappers socket()
ROLE
socket()
bind() (optional)
 The example is a simple client/server \network pipe" application that connect()
bind()
behaves as follows: listen()
accept()
send()/recv()
1. Starts an iterative daemon at a well-known server port 3: SERVICE send()/recv()
2. Client connects to the server and transmits its standard input to the close()
PROCESSING
close()
server
3. The server prints this data to its standard output
 The server portion of the \network pipe" application may actually run CLIENT
either locally or remotely... NETWORK SERVER

26 27
28
ACE C++ Wrapper Tutorial
Socket Client
#define PORT_NUM 10000

int
main (int argc, char *argv[]) {
ACE C++ Wrapper Tutorial

struct sockaddr_in saddr;


ROLE

close()
struct hostent *hp;
2: ACTIVE

SOCK_Stream

CLIENT
send()/recv()
char *host = argc > 1 ? argv[1] : "tango.cs.wustl.edu";
u_short port_num = argc > 2
SOCK_Connector

htons (argc > 2 ? atoi (argv[2]) : PORT_NUM);


char buf[BUFSIZ];
int s_fd;
int w_bytes;
int r_bytes;
3: SERVICE

NETWORK
PROCESSING

int n;

/* Create a local endpoint of communication */


s_fd = socket (PF_INET, SOCK_STREAM, 0);

/* Determine IP address of the server */


hp = gethostbyname (host);
ROLE

SERVER
close()
1: PASSIVE

SOCK_Stream

send()/recv()
SOCK_Acceptor
Network Pipe with ACE C++ Socket Wrappers

30
29




ACE C++ Wrapper Tutorial


Socket Client (cont'd)
e.g.,

/* Set up the address information to


contact the server */
% ./server &

memset ((void *) &saddr, 0, sizeof saddr);


saddr.sin_family = AF_INET;
ACE C++ Wrapper Tutorial

saddr.sin_port = port_num;
memcpy (&saddr.sin_addr, hp->h_addr, hp->h_length);

/* Establish connection with remote server */


connect (s_fd, (struct sockaddr *) &saddr,
sizeof saddr);

/* Send data to server (correctly handles


{ Operates at no loss of eciency

"incomplete writes" due to flow control) */


Complete example available at URL:

while ((r_bytes = read (0, buf, sizeof buf)) > 0)


% echo "hello world" | ./client localhost

for (w_bytes = 0; w_bytes < r_bytes; w_bytes += n)


client localhost.cs.wustl.edu%: hello world

n = write (s_fd, buf + w_bytes, r_bytes - w_bytes);

/* Explicitly close the connection */


{ www.cs.wustl.edu/schmidt/IPC SAP-92.ps.gz
Note that the ACE C++ Socket wrapper example:
{ Requires much less code (about 1/2 to 2/3 less)

close (s_fd);
{ Provides greater clarity and less potential for errors

return 0;
}
Running the Network Pipe Program

31
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
Socket Server C++ Socket Wrapper Client
#define PORT_NUM 10000 const u_short PORT_NUM = 10000;
int int main (int argc, char *argv[])
main (int argc, char *argv[]) {
{ char buf[BUFSIZ];
u_short port_num = char *host = argc > 1 ? argv[1] : "ics.uci.edu";
htons (argc > 1 ? atoi (argv[1]) : PORT_NUM); u_short port_num =
struct sockaddr_in saddr; htons (argc > 2 ? atoi (argv[2]) : PORT_NUM);
int s_fd, n_fd;
INET_Addr server_addr (port_num, host);
/* Create a local endpoint of communication */ SOCK_Stream cli_stream;
s_fd = socket (PF_INET, SOCK_STREAM, 0); SOCK_Connector connector.
/* Set up the address information to // Establish the connection with server.
become a server */ connector.connect (cli_stream, server_addr);
memset ((void *) &saddr, 0, sizeof saddr);
saddr.sin_family = AF_INET;
saddr.sin_port = port_num;
saddr.sin_addr.s_addr = INADDR_ANY;
/* Associate address with endpoint */
bind (s_fd, (struct sockaddr *) &saddr,
sizeof saddr);
/* Make endpoint listen for service requests */
listen (s_fd, 5);
34 32
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
Socket Server (cont'd) C++ Socket Wrapper Client (cont'd)
/* Performs the iterative server activities */ // Send data to server (correctly handles
for (;;) { // "incomplete writes").
char buf[BUFSIZ];
struct sockaddr_in cli_addr; for (;;) {
int r_bytes, cli_addr_len = sizeof cli_addr; ssize_t r_bytes = read (0, buf, sizeof buf);
struct hostent *hp; cli_stream.send_n (buf, r_bytes);
}
/* Create a new endpoint of communication */
while ((n_fd = accept (s_fd, (struct sockaddr *) // Explicitly close the connection.
&cli_addr, cli_stream.close ();
&cli_addr_len)) == -1 return 0;
&& errno == EINTR) }
continue;
if (n_fd == -1)
continue;
hp = gethostbyaddr ((char *) &cli_addr.sin_addr,
cli_addr_len, AF_INET);
printf ("client %s: ", hp->h_name), fflush (stdout);
/* Read data from client (terminate on error) */
while ((r_bytes = read (n_fd, buf, sizeof buf)) > 0)
write (1, buf, r_bytes);
/* Close the new endpoint
(listening endpoint remains open) */
close (n_fd);
}
}
35 33
38








ACE C++ Wrapper Tutorial


C++ Wrapper Socket Server
const u_short PORT_NUM = 10000;
// SOCK_SAP Server.
ACE C++ Wrapper Tutorial

int
main (int argc, char *argv[])
{
u_short port_num =
argc == 1 ? PORT_NUM : ::atoi (argv[1]);
Simplify for the common case

// Create a server.
SOCK_Acceptor acceptor ((INET_Addr) port_num);
Enforce typesafety at compile-time

Inline performance critical methods

SOCK_Stream new_stream;
INET_Addr cli_addr;
Allow controlled violations of typesafety

Enhance portability with parameterized types

De ne auxiliary classes to hide error-prone details


ACE C++ Wrapper Design Principles

Replace one-dimensional interfaces with hierarchical class categories


36

39
ACE C++ Wrapper Tutorial

// ...
C++ Wrapper Socket Server (cont'd)
// Performs the iterative server activities.
for (;;) {
char buf[BUFSIZ];

ACE C++ Wrapper Tutorial


// Create a new SOCK_Stream endpoint (note
// automatic restart if errno == EINTR).
acceptor.accept (new_stream, &cli_addr);

read (s_sd, buf, sizeof buf);

SOCK_Acceptor acceptor (port);


printf ("client %s: ", cli_addr.get_host_name ());

acceptor.recv (buf, sizeof buf);


fflush (stdout);

bind (s_sd, ...); // Bind address.

// Error not detected until run-time.


// Read data from client (terminate on error).
for (;;) {
ssize_t r_bytes;

int s_sd = socket (PF_INET, SOCK_STREAM, 0);

listen (s_sd); // Make a passive-mode socket.


r_bytes = new_stream.recv (buf, sizeof buf);

// Error: recv() not a method of SOCK_Acceptor.


if (r_bytes <= 0) break;
write (1, buf, r_bytes);
}
// Close new endpoint (listening

Sockets cannot detect certain errors at compile-time, e.g.,


// endpoint stays open).

ACE enforces type-safety at compile-time via factories, e.g.:


new_stream.close ();

Enforce Typesafety at Compile-Time


}
37
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
Allow Controlled Violations of Typesafety Supply Default Parameters
Make it easy to use the C++ Socket wrappers correctly, hard to use it SOCK_Connector (SOCK_Stream &new_stream,
const Addr &remote_sap,
incorrectly, but not impossible to use it in ways the class designers did not ACE_Time_Value *timeout = 0,
anticipate const Addr &local_sap = Addr::sap_any,
int protocol_family = PF_INET,
int protocol = 0);
 e.g., it may be necessary to retrieve the underlying socket handle:
The result is extremely concise for the common case:
fd_set rd_sds;
SOCK_Stream stream;
FD_ZERO (&rd_sds);
// Compiler supplies default values.
FD_SET (acceptor.get_handle (), &rd_sds); SOCK_Connector con (stream, INET_Addr (port, host));

select (acceptor.get_handle () + 1, &rd_sds, 0, 0, 0);

40 41

ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial


De ne Parsimonious Interfaces Combine Multiple Operations into One Operation
e.g., use LSOCK to pass socket handles: Creating a conventional passive-mode socket requires multiple calls:
LSOCK_Stream stream; int s_sd = socket (PF_INET, SOCK_STREAM, 0);
LSOCK_Acceptor acceptor ("/tmp/foo"); sockaddr_in addr;
memset (&addr, 0, sizeof addr);
acceptor.accept (stream);
addr.sin_family = AF_INET;
stream.send_handle (stream.get_handle ());
addr.sin_port = htons (port);
versus addr.sin_addr.s_addr = INADDR_ANY;
bind (s_sd, &addr, addr_len);
LSOCK::send_handle (const HANDLE sd) const { listen (s_sd);
u_char a[2]; iovec iov; msghdr send_msg;
// ...
a[0] = 0xab, a[1] = 0xcd;
iov.iov_base = (char *) a; iov.iov_len = sizeof a; SOCK Acceptor combines this into a single operation:
send_msg.msg_iov = &iov; send_msg.msg_iovlen = 1;
send_msg.msg_name = (char *) 0;
send_msg.msg_namelen = 0; SOCK_Acceptor acceptor ((INET_Addr) port);
send_msg.msg_accrights = (char *) &sd;
send_msg.msg_accrightslen = sizeof sd;
return sendmsg (this->get_handle (), &send_msg, 0);

42 43
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
Create Hierarchical Class Categories Enhance Portability with Parameterized Types
A A
IPC SOCK
SAP
DISTRIBUTED
APPLICATION1 DISTRIBUTED
APPLICATION2 DISTRIBUTED
APPLICATION3
APPLICATION 1 APPLICATION 2 APPLICATION 3

SOCK
COMMON INTERFACE
SOCK SOCK SOCK SOCK SOCK
Dgram Dgram CODgram Stream Connector Acceptor (PARAMETERIZED TYPES)
Bcast

SOCK_SAP TLI_SAP
SOCK
Dgram
LSOCK
Dgram
LSOCK
CODgram
LSOCK
Stream
LSOCK
Connector
LSOCK
Acceptor
BSD SOCKET
SOCKET SBSD
YSTEM V
SOCKET
USER
Mcast SPACE
API
API TLIAPIAPI
GROUP DATAGRAM A STREAM CONNECTION
COMM COMM COMM ESTABLISHMENT
LSOCK
OS KERNEL
KERNEL
PROTOCOL MECHANISMS
 Shared behavior is isolated in base classes (TCP/IP, OSI, ETC.)
SPACE

 Derived classes implement di erent communication services, NETWORK

communication domains, and connection roles INTERFACE

44 45

ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial


Enhance Portability with Parameterized Types (cont'd) Inline Performance Critical Methods
Switching wholesale between sockets and TLI simply requires instantiating Inlining is time and space ecient since key methods are very short:
a di erent C++ wrapper, e.g.,
class SOCK_Stream : public SOCK
// Conditionally select IPC mechanism. {
#if defined (USE_SOCKETS) public:
typedef SOCK_Acceptor PEER_ACCEPTOR; ssize_t send (const void *buf, size_t n)
#elif defined (USE_TLI) {
typedef TLI_Acceptor PEER_ACCEPTOR; return ACE_OS::send (this->get_handle (), buf, n);
#endif // USE_SOCKETS. }

int main (void) ssize_t recv (void *buf, size_t n)


{ {
// ... return ACE_OS::recv (this->get_handle (), buf, n);
}
// Invoke the echo_server with appropriate };
// network programming interfaces.
echo_server<PEER_ACCEPTOR> (port);
}

46 47
ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial
De ne Auxiliary Classes to Hide Error-Prone Details Summary of ACE C++ Socket Wrapper Design
Standard C socket addressing is awkward and error-prone Principles
 e.g., easy to neglect to zero-out a sockaddr in or convert port
 Domain analysis identi es and groups related classes of existing API
numbers to network byte-order, etc.
behavior
ACE C++ Socket Wrappers de ne classes to handle these details { Example subdomains include
class INET_Addr : public Addr {  Local context management and options, data transfer,
public:
INET_Addr (u_short port, long ip_addr = 0) {
connection/termination handling, etc.
memset (&this->inet_addr_, 0, sizeof this->inet_addr_);  Datagrams vs. streams
this->inet_addr_.sin_family = AF_INET;  Local vs. remote addressing
this->inet_addr_.sin_port = htons (port);
memcpy (&this->inet_addr_.sin_addr, &ip_addr, sizeof ip_addr);  Active vs. passive connection roles
}
// ...
private:  These relationships are directly re ected in the ACE C++ Socket
sockaddr_in inet_addr_; wrapper inheritance hierarchy
};

48 49

ACE C++ Wrapper Tutorial ACE C++ Wrapper Tutorial


Summary of ACE C++ Socket Wrapper Design Summary of ACE C++ Socket Wrapper Design
Principles (cont'd) Principles (cont'd)
 Performance improvements techniques include:
 The ACE C++ Socket wrappers are designed to maximize reusability
and sharing of components { Inline functions are used to avoid additional function call penalties
{ Dynamic binding is used sparingly to reduce time/space overhead
{ Inheritance is used to factor out commonality and decouple variation  i.e., it is eliminated for recv/send path
e.g.,  Note the di erence between the composition vs.
 Push common services \upwards" in the inheritance hierarchy
 Factor out variations in client/server portions of socket API
decomposition/composition aspects in design complexity
 Decouple datagram vs. stream operations, local vs. remote, etc. { i.e., ACE C++ Socket wrappers are primarily an exercise in
{ Inheritance also supports \functional subsetting" composition since the basic components already exist
 e.g., passing open le handles... { More complex OO designs involve both aspects...
 e.g., the ACE Streams, Service Con gurator, and Reactor
frameworks, etc.

50 51
ACE C++ Wrapper Tutorial
Concluding Remarks

 De ning C++ wrappers for native OS APIs simpli es the development


of correct, portable, and extensible applications
{ C++ inline functions ensure that performance isn't sacri ced
 ACE contains many C++ wrappers that encapsulate UNIX, Win32, and
RTOS APIs interfaces
{ e.g., sockets, TLI, named pipes, STREAM pipes, etc.
 ACE can be integrated conveniently with CORBA and DCOM provide a
exible high-performance, real-time development framework

52

You might also like