You are on page 1of 33

Overview of the Socket API (1/2)

Sockets are the most common network programming


API available on operating system platforms

•Originally developed in •A socket can be bound to a local or remote


BSD Unix as a C address
language API to TCP/IP •In Unix, socket handles & I/O handles can be
protocol suite used interchangeably in most cases, but this
•The Socket API has is not the case for Windows
approximately two dozen
functions classified in five
categories
•Socket is a handle
created by the OS that
associates it with an end
point of a communication
channel

1
Overview of the Socket API (2/2)

Local context
management

Connection
establishment &
termination

Data transfer
mechanisms

Options
management

Network
addressing
2
Taxonomy of Socket Dimensions
The Socket API can be decomposed into the following dimensions:
• Type of
communication
service
• e.g., streams versus
datagrams versus
connected datagrams
• Communication &
connection role
• e.g., clients often
initiate connections
actively, whereas
servers often accept
them passively
• Communication
domain
• e.g., local host only
versus local or remote
host
3
Limitations with the Socket APIs (1/2)
Poorly structured, non-uniform, & non-portable
• API is linear rather than hierarchical
• i.e., the API is not structured according to the different phases of
connection lifecycle management and the roles played by the
participants
• No consistency among the names
• Non-portable & error-prone
• Function names: read() & write() used for any I/O handle on
Unix but Windows needs ReadFile() & WriteFile()
• Function semantics: different behavior of same function on different
OS e.g., accept () can take NULL client address parameter on
Unix/Windows, but will crash on some operating systems, such as
VxWorks
• Socket handle representations: different platforms represent
sockets differently e.g., Unix uses unsigned integers whereas
Windows uses pointers
• Header files: Different platforms use different names for header files
for the socket API
4
Limitations with the Socket APIs (2/2)
Lack of type safety
• I/O handles are not amenable to strong type checking at compile
time
• e.g., no type distinction between a socket used for passive
listening & a socket used for data transfer

Steep learning curve due to complex semantics


• Multiple protocol families & address families
• Options for infrequently used features such as broadcasting, async
I/O, non blocking I/O, urgent data delivery
• Communication optimizations such as scatter-read & gather-write
• Different communication and connection roles, such as active &
passive connection establishment, & data transfer

Too many low-level details


• Forgetting to use the network byte order before data transfer
• Possibility of missing a function, such as listen()
• Possibility of mismatch between protocol & address families
• Forgetting to initialize underlying C structures e.g., sockaddr
• Using a wrong socket for a given role
5
Example of Socket API Limitations (1/3)
1 #include <sys/types.h> Possible differences in header
file names
2 #include <sys/socket.h>
3
4 const int PORT_NUM = 10000;
5
6 int echo_server ()
7 {
8 struct sockaddr_in addr;
Forgot to initialize to sizeof
9 int addr_len; (sockaddr_in)
10 char buf[BUFSIZ];
11 int n_handle; Use of non-portable handle type
12 // Create the local endpoint.

6
Example of Socket API Limitations (2/3)
13 int s_handle = socket (PF_UNIX, SOCK_DGRAM, 0);
14 if (s_handle == -1) return -1;
15 Use of non-portable return value
16 // Set up address information where server listens.
Protocol and address family
17 addr.sin_family = AF_INET; mismatch
18 addr.sin_port = PORT_NUM; Wrong byte order
19 addr.sin_addr.addr = INADDR_ANY;
Unused structure members not
20 zeroed out
21 if (bind (s_handle, (struct sockaddr *) &addr,
22 sizeof addr) == -1)
23 return -1;
24 Missed call to listen()

7
Example of Socket API Limitations (3/3)
25 // Create a new communication endpoint.
26 if (n_handle = accept (s_handle, (struct sockaddr *) &addr,
27 &addr_len) != -1) {
28 int n; SOCK_DGRAM handle illegal here

29 while ((n = read (s_handle, buf, sizeof buf)) > 0)


30 write (n_handle, buf, n); Reading from wrong handle
31 No guarantee that “n” bytes will be written
32 close (n_handle);
33 }
34 return 0;
35 }

8
ACE Socket Wrapper Façade Classes
ACE defines a set of C++
classes that address the
limitations with the Socket
API
• Enhance type-safety
• Ensure portability
• Simplify common use
cases
• Building blocks for
higher-level
abstractions

These classes are


designed in accordance
with the Wrapper
Facade design pattern

9
The Wrapper Façade Pattern (1/2)
Context
• Networked applications must
manage a variety of OS
services, including processes,
threads, socket connections,
virtual memory, & files Applications

• OS platforms provide low-


level APIs written in C to
access these services

Problem
Solaris VxWorks
• The diversity of hardware &
operating systems makes it hard to
Win2K Linux LynxOS
build portable & robust networked
application software
• Programming directly to low-level OS
APIs is tedious, error-prone, & non-
10 portable
The Wrapper Façade Pattern (2/2)
Solution
• Apply the Wrapper Facade design pattern (P2) to avoid
accessing low-level operating system APIs directly

calls
Wrapper Facade API FunctionA()
calls methods
Application data calls
API FunctionB()
method1()
… calls
methodN() API FunctionC()

This pattern void method1(){


functionA();
void methodN(){
functionA();
encapsulates data & }
functionB(); }

functions provided by
existing non-OO APIs
within more concise,
: Application : Wrapper : APIFunctionA : APIFunctionB
robust, portable, Facade
maintainable, & cohesive method()
functionA()
OO class interfaces
functionB()

11
ACE Socket Wrapper Façades Taxonomy
•The structure of the
ACE Socket wrapper
facades reflects the
domain of networked
IPC properties
•The ACE Socket
wrapper façade classes
provide the following
capabilities:
•ACE_SOCK_*
classes encapsulate
Internet-domain
Socket API
functionality
•ACE_LSOCK_*
classes encapsulate
UNIX-domain Socket •ACE also has wrapper facades for datagrams
API functionality •e.g., unicast, multicast, broadcast
12
Roles in the ACE Socket Wrapper Facade

•The active connection role (ACE_SOCK_Connector) is played by


a peer application that initiates a connection to a remote peer
•The passive connection role (ACE_SOCK_Acceptor) is played by
a peer application that accepts a connection from a remote peer &
•The communication role (ACE_SOCK_Stream) is played by both
13 peer applications to exchange data after they are connected
ACE Socket Addressing Classes (1/2)
Motivation
• Network addressing is a trouble spot in the Socket API
• To minimize the complexity of these low-level details,
ACE defines a hierarchy of classes that provide a
uniform interface for all ACE network addressing
objects

14
ACE Socket Addressing Classes (2/2)

Class Capabilities
•The ACE_Addr class is the
root of the ACE network
addressing hierarchy
•The ACE_INET_Addr
class represents TCP/IP &
UDP/IP addressing
information
•This class eliminates
many subtle sources of
accidental complexity

15
ACE I/O Handle Classes (1/2)
Motivation
• Low-level C I/O handle types are tedious, error-prone, & non-portable
• Even the ACE_HANDLE typedef is still not sufficiently object-oriented &
typesafe
int buggy_echo_server (u_short port_num) {
sockaddr_in s_addr;
int acceptor = socket (PF_UNIX, SOCK_DGRAM, 0);
int is not portable to Windows
s_addr.sin_family = AF_INET;
s_addr.sin_port = port_num;
s_addr.sin_addr.s_addr = INADDR_ANY;
bind (acceptor, (sockaddr *) &s_addr, sizeof s_addr);
int handle = accept (acceptor, 0, 0);
for (;;) {
char buf[BUFSIZ];
ssize_t n = read (acceptor, buf, sizeof buf);
if (n <= 0) break; Reading from wrong handle
write (handle, buf, n);
}
}
16
ACE I/O Handle Classes (2/2)
Class Capabilities
•ACE_IPC_SAP is the root of
the ACE hierarchy of IPC
wrapper facades
•It provides basic I/O handle
manipulation capabilities to
other ACE IPC wrapper
facades
•ACE_SOCK is the root of the
ACE Socket wrapper facades
& it provides methods to
•Create & destroy socket
handles
•Obtain the network addresses of local & remote peers
•Set/get socket options, such as socket queue sizes,
•Enable broadcast/multicast communication
17
•Disable Nagle‘s algorithm
The ACE_SOCK_Connector Class
Motivation
•There is a confusing asymmetry in the Socket API between (1)
connection roles & (2) socket modes
•e.g., an application may accidentally call recv() or send() on
a data-mode socket handle before it's connected
•This problem can't be detected until run time since C socket
handles are weakly-typed
int buggy_echo_client (u_short port_num, const char *s)
{
int handle = socket (PF_UNIX, SOCK_DGRAM, 0);

write (handle, s, strlen (s) + 1);

sockaddr_in s_addr;
Operations called in
memset (&s_addr, 0, sizeof s_addr);
wrong order
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons (port_num);
connect (handle, (sockaddr *) &s_addr, sizeof s_addr);
}
18
The ACE_SOCK_Connector Class
Class Capabilities
•ACE_SOCK_Connector is factory that establishes a new endpoint of
communication actively & provides capabilities to
•Initiate a connection with a peer acceptor & then to initialize an
ACE_SOCK_Stream object after the connection is established
•Initiate connections in either a blocking, nonblocking, or timed manner
•Use C++ traits to support
generic programming
techniques that enable
wholesale replacement of
IPC functionality

19
Sidebar: Traits for ACE Wrapper Facades (1/2)

•ACE uses the C++ generic programming idiom to define &


combine a set of characteristics to alter the behavior of a
template class
•In C++, the typedef & typename language feature is used to
define a trait
•A trait provides a convenient way to associate related types,
values, & functions with template parameter type without
requiring that they be defined as members of the type
•Traits are used extensively in the C++ Standard Template
Library (STL)

20
Sidebar: Traits for ACE Wrapper Facades (2/2)
•ACE Socket wrapper facades use traits to define the following
associations
•PEER_ADDR – this trait defines the ACE_INET_Addr class associated
with the ACE Socket Wrapper Façade
•PEER_STREAM – this trait defines the ACE_SOCK_Stream data
transfer class associated with the ACE_SOCK_Acceptor &
ACE_SOCK_Connector factories
class ACE_TLI_Connector {
class ACE_SOCK_Connector { public:
public: typedef ACE_INET_Addr
typedef ACE_INET_Addr PEER_ADDR;
PEER_ADDR; typedef ACE_TLI_Stream
typedef ACE_SOCK_Stream PEER_STREAM;
PEER_STREAM; // ...
// ...

21
Using the ACE_SOCK_Connector (1/3)
•This example shows how the ACE_SOCK_Connector can be used
to connect a client application to a Web server
int main (int argc,
char *argv[]) {
const char *pathname =
argc > 1
? argv[1] : “/index.html";
const char *server_hostname = • Instantiate the connector,
argc > 2 data transfer, & address
? argv[2] : “www.dre.vanderbilt.edu"; objects
typedef ACE_SOCK_Connector CONNECTOR;
CONNECTOR connector;
CONNECTOR::PEER_STREAM peer;
CONNECTOR::PEER_ADDR peer_addr;

if (peer_addr.set (80, server_hostname) == -1) • Block until


return 1; connection
else if (connector.connect (peer, established or
peer_addr) == -1) connection request
return 1; failure
22
Using the ACE_SOCK_Connector (2/3)
// Designate a nonblocking connect.
if (connector.connect (peer, • Perform a non-blocking
peer_addr, connect
&ACE_Time_Value::zero) == -1) {
if (errno == EWOULDBLOCK) {
// Do some other work ...

// Now, try to complete connection establishment,


// but don't block if it isn't complete yet.
if (connector.complete (peer,
• If connection not
0,
established, do other &ACE_Time_Value::zero) == -1)
work & try again without
blocking

// Designate a timed connect.


ACE_Time_Value timeout (10); // 10 second timeout.
if (connector.connect (peer,
• Perform a timed connect
peer_addr, e.g., 10 seconds in this
&timeout) == -1) { case
if (errno == ETIME) {
23 // Timeout, do something else
Using the ACE_SOCK_Connector (3/3)

•The ACE_SOCK_Connector can be passed the following values to


control its timeout behavior

24
The ACE_SOCK_Stream Class (1/2)
Motivation
•Developers can misuse sockets in ways that can't be detected during
compilation
•An ACE_SOCK_Stream object can't be used in any role other than
data transfer without violating its (statically type-checked) interface
int buggy_echo_server (u_short port_num) {
sockaddr_in s_addr;
int acceptor = socket (PF_UNIX, SOCK_DGRAM, 0);
s_addr.sin_family = AF_INET;
s_addr.sin_port = port_num;
s_addr.sin_addr.s_addr = INADDR_ANY;
bind (acceptor, (sockaddr *) &s_addr, sizeof s_addr);
int handle = accept (acceptor, 0, 0);
for (;;) {
char buf[BUFSIZ];
ssize_t n = read (acceptor, buf, sizeof buf);
if (n <= 0) break;
Reading from wrong handle
write (handle, buf, n);
}
}
25
The ACE_SOCK_Stream Class (2/2)
Class Capabilities
•Encapsulates data transfer
mechanisms supported by
data-mode sockets to provide
the following capabilities:
•Support for sending &
receiving up to n bytes or
exactly n bytes
•Support for “scatter-read,”
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, & timed I/O operations
•Support for generic programming techniques that enable the wholesale
replacement of functionality via C++ parameterized types
26
Using the ACE_SOCK_Stream (1/2)
•This example shows how an ACE_SOCK_Stream can be used to send &
receive data to & from a Web server
// ...Connection code from example in Section 3.5 omitted...
char buf[BUFSIZ]; • Initialize the iovec
iovec iov[3]; vector for scatter-read
iov[0].iov_base = (char *) "GET "; & gather-write I/O
iov[0].iov_len = 4; // Length of "GET ".
iov[1].iov_base = (char *) pathname;
iov[1].iov_len = strlen (pathname);
iov[2].iov_base = (char *) " 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) • Perform blocking gather-


return 1; write on ACE_SOCK_Stream

for (ssize_t n; (n = peer.recv (buf, sizeof buf)) > 0; )


ACE::write_n (ACE_STDOUT, buf, n);

return peer.close () == -1 ? 1 : 0; • Perform


blocking read on
} ACE_SOCK_Stream
27
Using the ACE_SOCK_Stream (2/2)
•Blocking & non-blocking I/O semantics can be controlled via the
ACE_SOCK_STREAM enable() & disable() methods, e.g.,
•peer.enable (ACE_NONBLOCK); // enables non blocking
peer.disable (ACE_NONBLOCK); // disable non blocking
• If the I/O operation blocks, it returns a -1 & errno is set to
EWOULDBLOCK

•I/O operations can involve timeouts, e.g.,


ACE_Time_Value timeout (10); // 10 second timeout
If (peer.sendv_n (iov, 3, &timeout) == -1) {
// check if errno is set to ETIME,
// which indicates a timeout
}
// similarly use timeout for receiving data

28
Sidebar: Working with (& Around) Nagle’s Algorithm
Nagle’s Algorithm
• Problem: Need to tackle the send-side silly window syndrome, where small
data payloads, such as a keystroke, result in transmissions of large packets
& causing unnecessary waste of network resources & congestion
• Solution: The OS kernel buffers a # of small-sized application messages &
concatenates them into a larger size packet that can then be transmitted
• Consequences: Although network congestion is minimized, it can lead to
higher & unpredictable latencies, as well as lower throughput
Controlling Nagle’s Algorithm via ACE
• Use the set_option() method of the ACE_SOCK class e.g.,
int nodelay = 1; // Disable Nagle’s algorithm
ACE_SOCK_Stream option_setter (handle);
if (-1 == option_setter.set_option (ACE_IPPROTO_TCP,
TCP_NODELAY,
&nodelay,
sizeof (nodelay)))
...
29
The ACE_SOCK_Acceptor Class (1/2)
Motivation
•The C functions in the Socket API are weakly typed, which makes it easy
to apply them incorrectly in ways that can’t be detected until run-time
•The ACE_SOCK_Acceptor class ensures type errors are detected at
compile-time
int buggy_echo_server (u_short port_num) {
sockaddr_in s_addr;
int acceptor = socket (PF_UNIX, SOCK_DGRAM, 0);
s_addr.sin_family = AF_INET;
s_addr.sin_port = port_num;
s_addr.sin_addr.s_addr = INADDR_ANY;
bind (acceptor, (sockaddr *) &s_addr, sizeof s_addr);
int handle = accept (acceptor, 0, 0);
for (;;) {
char buf[BUFSIZ];
ssize_t n = read (acceptor, buf, sizeof buf);
if (n <= 0) break; Reading from wrong handle
write (handle, buf, n);
}
30 }
The ACE_SOCK_Acceptor Class (2/2)

Class Capabilities
•This class is a factory
that establishes a new
endpoint of
communication
passively & provides
the following
capabilities:

•It accepts a connection from a peer connector & then initializes an


ACE_SOCK_Stream object after the connection is established
•Connections can be accepted in either a blocking, nonblocking, or
timed manner
•C++ traits are used to support generic programming techniques that
enable the wholesale replacement of functionality via C++
parameterized types
31
Using the ACE_SOCK_Acceptor
• This example shows how an ACE_SOCK_Acceptor & ACE_SOCK_Stream can
be used to accept connections & send/receive data to/from a web client
extern char *get_url_pathname (ACE_SOCK_Stream *);
int main ()
{ • Instantiate the acceptor, data transfer, & address objects
ACE_INET_Addr server_addr;
ACE_SOCK_Acceptor acceptor; • Initialize a passive
ACE_SOCK_Stream peer; mode endpoint to
listen for connections
if (server_addr.set (80) == -1) return 1; on port 80
if (acceptor.open (server_addr) == -1) return 1;
• Accept a new connection
for (;;) {
if (acceptor.accept (peer) == -1) return 1;
peer.disable (ACE_NONBLOCK); // Ensure blocking <send_n>.
ACE_Auto_Array_Ptr<char *> pathname (get_url_pathname (peer));
ACE_Mem_Map mapped_file (pathname.get ()); • Send the
if (peer.send_n (mapped_file.addr (), requested data
mapped_file.size ()) == -1) return 1;
peer.close (); • Close the connection to the sender
}
• Stop receiving any
return acceptor.close () == -1 ? 1 : 0; connections
}32
Sidebar: The ACE_Mem_Map Class
Memory Mapped Files ACE_Mem_Map Class
•Many modern operating systems provide a •A wrapper façade that
mechanism for mapping a file’s contents encapsulates the memory
directly into a process’s virtual address mapped file system
space mechanisms on different
•This memory-mapped file mechanism can operating systems
be read from or written to directly by •Relieves application
referencing the virtual memory developers from having to
•e.g., via pointers instead of using less manually perform
efficient I/O functions bookkeeping tasks
•The file manager defers all read/write •e.g., explicitly opening
operations to the virtual memory manager files or determining their
lengths
•Contents of memory mapped files can be
shared by multiple processes on the same •The ACE_Mem_Map class
machine offers multiple constructors
with several signature
•It can also be used to provide a persistent variants
33
backing store

You might also like