Sie sind auf Seite 1von 150



Socket programming


Socket programming
© Copyright International Business Machines Corporation 2000. All rights reserved.
US Government Users Restricted Rights – Use, duplication or disclosure restricted by GSA ADP Schedule Contract
with IBM Corp.
Contents
Part 1. Socket programming . . . . . 1 Major components of DNS for sockets . . . . . 57
Format of DNS queries and responses for sockets . 57
Format of the DNS header section for sockets . . 62
Chapter 1. Print this topic . . . . . . . 3 Format of the DNS question section for sockets 63
Format of the DNS answer, authority, and
Chapter 2. What is a socket?. . . . . . 5 additional sections for sockets . . . . . . . 65
When to use IP-to-LU mapping. . . . . . . . 66
Chapter 3. How do sockets work? . . . 7
OS/400 socket and BSD implementation differences 10 Chapter 7. Examples: Using sockets . . 69
Using AS/400 client SOCKS support to Example: Making a connection using the telephony
communicate outside a firewall. . . . . . . . 12 domain socket . . . . . . . . . . . . . 70
AnyNet support for sockets . . . . . . . . . 16 Example: Accepting a connection using the
telephony domain socket . . . . . . . . . . 72
Chapter 4. Socket address structure Example: Connecting a single IP network with a
and characteristics . . . . . . . . . 19 single SNA network . . . . . . . . . . . 74
Example: Connecting multiple IP networks with
Address family protocols for socket() . . . . . . 19
multiple SNA networks . . . . . . . . . . 77
Type parameter for socket() . . . . . . . . . 22
Example: Changing a configuration . . . . . . 82
Examples: TCP connection-oriented client/server
Chapter 5. Setting up the socket introduction . . . . . . . . . . . . . . 83
application environment . . . . . . . 25 Examples: Connecting a TCP server and client. . 84
Requirements for configuring a socket environment 26 Examples: Connecting an SIOCSENDQ server
Prerequisite information to configure IP over and client . . . . . . . . . . . . . . 90
SNA . . . . . . . . . . . . . . . . 26 Examples: UDP connectionless client/server
Configuring the socket environment . . . . . 27 introduction . . . . . . . . . . . . . . 94
Debugging configuration for IP over SNA . . . 29 Examples: Connecting a UDP server and client 95
Configuring AS/400 client SOCKS support . . . 30 Examples: Connection-oriented server designs
Configuring AF_TELEPHONY sockets to introduction . . . . . . . . . . . . . . 99
telephony domain sockets . . . . . . . . 30 Example: Writing an iterative server program 101
Configuring UNIX domain sockets . . . . . 33 Examples: Using the spawn() API to create child
Setting the out-of-band data for sockets . . . . 34 processes . . . . . . . . . . . . . . 103
Coding aspects of the socket environment . . . . 37 Examples: Using givedescriptor() and
Socket header files . . . . . . . . . . . 37 takedescriptor() APIs to handle incoming
SSL programming protocols . . . . . . . . 43 connections . . . . . . . . . . . . . 107
Using sendmsg() and recvmsg() to pass Examples: Using sendmsg() and recvmsg() APIs
descriptors . . . . . . . . . . . . . 44 to handle incoming connections . . . . . . 113
Using send_file() and accept_and_recv() APIs . . 46 Examples: Using multiple accept() APIs to
handle incoming connection requests . . . . 118
Chapter 6. Improving performance of Example: Code for the connection-oriented
the socket environment . . . . . . . 49 common client . . . . . . . . . . . . . 122
Socket network functions . . . . . . . . . . 49 Example: Threadsafe network routines for using
Optimal setting of the listen() back_log parameter 51 gethostbyaddr_r() . . . . . . . . . . . . 123
sendmsg() and recvmsg() API considerations . . . 51 Examples: Sending and receiving a multicast
Error information for the sendmsg() API. . . . 53 datagram . . . . . . . . . . . . . . . 125
Error information for the recvmsg() API . . . . 54 Example: Establishing SSL server and client
Using sockets to signal applications . . . . . . 55 communications . . . . . . . . . . . . 129
Returning blocked socket functions . . . . . . 56 Examples: Establishing communications using the
Using select() for I/O multiplexing . . . . . . 57 send_file() and accept_and_recv() APIs . . . . . 136

© Copyright IBM Corp. 2000 iii


iv Socket programming
Part 1. Socket programming
Socket programming shows how to use socket application program interfaces
(APIs) to establish communication links between remote and local processes.
Socket programming information is intended for programmers who use the
Integrated Language Environment (ILE) C for AS/400 language to develop
applications that handle socket functions.

As of V4R3, socket system functions and the socket network functions are
threadsafe.

View and print a PDF version of this topic:

“Chapter 1. Print this topic” on page 3

Socket programming topics:

For information on how to develop a socket application or to obtain detailed


socket conceptual information and configuration programming examples, see:
v “Chapter 2. What is a socket?” on page 5
v “Chapter 3. How do sockets work?” on page 7
v “Chapter 4. Socket address structure and characteristics” on page 19
v “Chapter 5. Setting up the socket application environment” on page 25
v “Chapter 6. Improving performance of the socket environment” on page 49
v “Chapter 7. Examples: Using sockets” on page 69

Socket API reference information:

Socket API tables

© Copyright IBM Corp. 2000 1


2 Socket programming
Chapter 1. Print this topic
You can view or download a PDF version of this document for viewing or
printing. You must have Adobe® Acrobat® Reader installed to view PDF files. You
can download a copy from

http://www.adobe.com/prodindex/acrobat/readstep.html .

To view or download the PDF version, select Socket programming.

To save a PDF on your workstation for viewing or printing:


1. Open the PDF in your browser (click the link above).
2. In the menu of your browser, click File.
3. Click Save As...
4. Navigate to the directory in which you would like to save the PDF.
5. Click Save.

© Copyright IBM Corp. 2000 3


4 Socket programming
Chapter 2. What is a socket?
A socket is a communications connection point (endpoint) that you can name and
address in a network. The processes that use a socket can reside on the same
system or on different systems on different networks. Sockets are useful for both
stand-alone and network applications.

Sockets commonly are used for client/server interaction. Typical system


configuration places the server on one machine, with the clients on other machines.
The clients connect to the server, exchange information, and then disconnect.

Socket characteristics:

Sockets share the following characteristics:


v A socket is represented by an integer. That integer is called a socket descriptor.
v A socket exists as long as the process maintains an open link to the socket.
v You can name a socket and use it to communicate with other sockets in a
communication domain.
v Sockets perform the communication when the server accepts connections from
them, or when it exchanges messages with them.
v You can create sockets in pairs.

The connection that a socket provides can be connection-oriented or connectionless.


Connection-oriented communication implies that a connection is established, and a
dialog between the programs will follow. The program that provides the service
(the server program) establishes the connection. It assigns itself a name that
identifies where to obtain that service. The client of the service (the client program)
must request the service of the server program. The client does this by connecting
to the distinct name that the server program has designated. It is similar to dialing
a telephone number (an identifier) and making a connection with another party
that is offering a service (for example, a plumber). When the receiver of the call
(the server) answers the telephone, the connection is established. The plumber
verifies that you have reached the correct party, and the connection remains active
as long as both parties require it.

Connectionless communication implies that no connection is established over


which a dialog or data transfer can take place. Instead, the server program
designates a name that identifies where to reach it (much like a post office box). By
sending a letter to a post office box, you cannot be absolutely sure the letter is
received. You may have to send another letter to reestablish communication.

© Copyright IBM Corp. 2000 5


6 Socket programming
Chapter 3. How do sockets work?
A socket has a typical flow of events. In a connection-oriented client-to-server
model, the socket on the server process waits for requests from a client. To do this,
the server first establishes (binds) an address that clients can use to find the server.
When the address is established, the server waits for clients to request a service.
The server performs the client’s request and sends the reply back to the client. The
two endpoints establish a connection, and bring the client and server together. The
client-to-server data exchange takes place when a client connects to the server
through a socket.

Note: IBM based the AS/400 system sockets on Berkeley Software Distributions
(BSD) 4.3 sockets. The two types of sockets are compatible, but differences
between OS/400 sockets and BSD sockets do exist.

The following figure shows the typical flow of events (and the sequence of issued
functions) for a connection-oriented socket session. An explanation of each event

© Copyright IBM Corp. 2000 7


follows the figure.

8 Socket programming
1. The socket() function creates an endpoint for communications and returns a
socket descriptor that represents the endpoint.
2. When an application has a socket descriptor, it can bind a unique name to the
socket. Servers must bind a name to be accessible from the network.
3. The listen() function indicates a willingness to accept client connection
requests. When a listen() is issued for a socket, that socket cannot actively
initiate connection requests. The listen() API is issued after a socket is allocated
with a socket() function and the bind() function binds a name to the socket. A
listen() function must be issued before an accept() function is issued.
4. The client application uses a connect() function on a stream socket to establish
a connection to the server.
5. Servers use stream sockets to accept a client connection request with the
accept() function. The server must issue the bind() and listen() functions
successfully before it can issue an accept() function to accept an incoming
connection request.
6. When a connection is established between stream sockets (between client and
server), you can use any of the socket API data transfer functions. Clients and
servers have many data transfer functions from which to choose, such as
send(), recv(), read(), write(), and others.
7. When a server or client wants to cease operations, it issues a close() function to
release any system resources acquired by the socket.

Note:

The socket APIs are located in the communications model between the
application layer and the transport layer. The socket APIs are not a layer in
the communication model. Socket APIs allow applications to interact with
the transport or networking layers of the typical communications model.
The arrows in the following figure show the position of a socket, and the
communication layer that the socket provides.

Typically, a network configuration does not allow connections between a secure


internal network and a less secure external network. However, you can enable
sockets to communicate with server programs that run on a system outside of a
firewall (a very secure host).

Chapter 3. How do sockets work? 9


Sockets are also a part of IBM’s AnyNet implementation for the Multiprotocol
Transport Networking (MPTN) architecture. MPTN architecture provides the ability
to operate a transport network over additional transport networks and to connect
application programs across transport networks of different types.

The Information Center provides specific OS/400 API reference information and
general API concepts. The UNIX-type APIs contain the information that pertains to
socket APIs.

OS/400 socket and BSD implementation differences


Sockets are a Berkeley Software Distributions (BSD) interface. The semantics, such
as the return codes that an application receives and the arguments available on
supported functions, are BSD semantics. Some BSD semantics are not available in
the OS/400 implementation, and you may need to make changes to a typical BSD
socket application to run it on AS/400 systems.

The following list summarizes the differences between the OS/400 implementation
and the BSD implementation:
v /etc/hosts, /etc/services, /etc/networks, and /etc/protocols
For these files, the OS/400 implementation supplies the following database files,
which serve the same functions, respectively.

QUSRSYS File Contents


QATOCHOST List of host names and the corresponding IP addresses.
QATOCPP List of protocols that are used in the Internet.
QATOCPS List of services, and the specific port and protocol that a service
uses
QATOCPN List of networks and the corresponding IP addresses.

v /etc/resolv.conf
Configure this information for OS/400 implementation by using the CFGTCP
menu.
v bind()
On a BSD system, a client can do the following:
– Create an AF_UNIX socket by using socket()
– Connect to a server by using connect()
– Bind a name to its socket by using bind()
The OS/400 implementation does not support this scenario (the bind() fails).
v close()
The OS/400 implementation supports the linger timer for close(), except for
AF_INET sockets over SNA. Some BSD implementations do not support the
linger timer for close().
v connect()
On a BSD system, the socket is no longer connected when all of these conditions
are met:
1. You issue a connect() against a socket that was previously connected to an
address.
2. The socket is using a connectionless transport service.
3. You do not use a valid address or address length.

10 Socket programming
The OS/400 implementation does not support this scenario (the connect() fails
and the socket remains connected).

You can disconnect a connectionless transport socket for which you issued a
connect() by setting the address_length parameter to zero and issuing another
connect().
v ioctl()
– On a BSD system, with a socket type of SOCK_DGRAM, the FIONREAD
request returns the length of the data plus the length of the address. On the
OS/400 implementation, FIONREAD only returns the length of data.
– Not all requests available on most BSD implementations of ioctl() are
available on the OS/400 implementation of ioctl().
v listen()
On a BSD system, issuing a listen() with the backlog parameter set to a value
that is less than zero or greater than {SOMAXCONN} does not result in an error.
The BSD implementation, in some cases, does not use the backlog parameter, but
uses an algorithm to determine the final result for the backlog value. The
OS/400 implementation returns an error if both of the following occur:
1. The backlog value is not between zero and {SOMAXCONN}
2. Setting the backlog to a valid value results in the system using in the value
as the backlog
v OOB data
In the OS/400 implementation, the system does not discard OOB data under the
following conditions:
1. You do not set SO_OOBINLINE.
2. The system received OOB data.
3. You then set SO_OOBINLINE on.
The system considers the initial OOB byte normal data.
v protocol parameter of socket()
As a means of providing additional security, no user can create a SOCK_RAW
socket specifying a protocol of IPPROTO_TCP or IPPROTO_UDP.
v res_xlate() and res_close()
These functions are included in the resolver routines for the OS/400
implementation. res_xlate() translates DNS packets from EBCDIC to ASCII and
from ASCII to EBCDIC. You can use res_close() to close a socket that was used
by res_send() with the RES_STAYOPEN option set. It also resets the _res
structure.
v sendmsg() and recvmsg()
The OS/400 implementation of sendmsg() and recvmsg() allows up to and
including {MSG_MAXIOVLEN} I/O vectors. The BSD implementation allows
{MSG_MAXIOVLEN - 1} I/O vectors.
v shutdown()
The OS/400 implementation of shutdown() may block if an output function is
currently blocked on the socket descriptor. On a BSD implementation, the
blocking output function ends with the [EPIPE] errno value.
Similarly, a BSD implementation ends blocking input operations with a zero
output value when they are blocking and another process or thread issues a
shutdown(). The OS/400 implementation simply fails any subsequent input

Chapter 3. How do sockets work? 11


function with a zero output value. However, the blocking input function
continues to block until data is received, or some other action is taken to remove
it from a waiting state.
v Signals
Here are a few differences relating to signal support:
– BSD implementations issue a SIGIO signal each time an acknowledgement is
received for data that is sent on an output operation. The OS/400 sockets
implementation does not generate signals related to outbound data.
– The default action for the SIGPIPE signal is to end the process in BSD
implementations. To maintain downward compatibility with previous releases
of OS/400, the OS/400 implementation uses a default action of ignore for the
SIGPIPE signal.
v SO_REUSEADDR option
On BSD systems, a connect() on a socket of family AF_INET and type
SOCK_DGRAM causes the system to change the address to which the socket is
bound to the address of the interface that is used to reach the address specified
on the connect(). For example, if you bind a socket of type SOCK_DGRAM to
address INADDR_ANY, and then connect it to an address of a.b.c.d, the system
changes your socket so it is now bound to the IP address of the interface that
was chosen to route packets to address a.b.c.d.. If this IP address that the socket
is bound to is a.b.c.e, address a.b.c.e now appears on getsockname() instead of
INADDR_ANY. You must use the SO_REUSEADDR option to bind any other
sockets to the same port number with an address of a.b.c.e.
In contrast, in this example, the OS/400 implementation does not change the
local address from INADDR_ANY to a.b.c.e. getsockname() continues to return
INADDR_ANY after the connect() is performed.
v SO_SNDBUF and SO_RCVBUF options
The values set for SO_SNDBUF and SO_RCVBUF on a BSD system provide a
greater level of control than on an OS/400 implementation. On an OS/400
implementation, these values are taken as advisory values.

Using AS/400 client SOCKS support to communicate outside a firewall


AS/400 client SOCKS support enables programs that use (address family AF_INET,
type SOCK_STREAM) sockets to communicate with server programs that run on
systems outside a firewall. A firewall is a very secure host that a network
implementer places between a secure internal network and a less secure external
network. Typically such a network configuration does not allow communications
that originate from the secure host to route on the less secure network, and
vice-versa. Proxy servers that exist on the firewall help manage required access
between secure hosts and less secure networks.

Applications that run on hosts in a secure internal network must send their
requests to firewall proxy servers to navigate the firewall. The proxy servers can
then forward these requests to the real server on the less secure network.
Additionally, they can relay the reply back to the applications on the originating
host. A common example of a proxy server is an HTTP proxy server. Proxy servers
perform a number of tasks for HTTP clients:
v They hide your internal network from outside systems.
v They protect the host from direct access by outside systems.
v They can filter data that comes in from outside if properly designed and
configured.

12 Socket programming
HTTP proxy servers handle only HTTP clients.

A common alternative to running multiple proxy servers on a firewall is to run a


more robust proxy server known as a SOCKS server. A SOCKS server can act as a
proxy for any TCP client connection that is established by using the sockets API.
The key advantage to AS/400 client SOCKS support is that it enables client
applications to access a SOCKS server transparently without changing any client
code.

The following figure shows a common firewall arrangement with an HTTP proxy,
a Telnet proxy, and a SOCKS proxy on the firewall. Notice that two separate TCP
connections are used for the secure client that is accessing a server on the Internet.
One connection leads from the secure host to the SOCKS server, and the other
leads from the less secure network to the SOCKS server. See “Configuring AS/400
client SOCKS support” on page 30 to establish the secure client host to a SOCKS
server.

When configured, the system automatically directs certain outbound connections to


the SOCKS server that you specified on the SOCKS page. You do not need to make
any changes to the secure client application. When the client receives the request,

Chapter 3. How do sockets work? 13


the SOCKS server establishes a separate external TCP/IP connection to the server
in the less secure network. The SOCKS server then relays data between the internal
and external TCP/IP connections.

Note: The remote host on the less secure network connects directly to the SOCKS
server. It does not have direct access to the secure client.
Up to this point only outbound TCP connections that originate from the secure
client were discussed. AS/400 client SOCKS support also lets you tell the SOCKS
server to allow an inbound connection request across a firewall. An Rbind() call
from the secure client system allows this communication. In order for Rbind() to
operate, the Secure client must issue a connect() call, and the call must result in an
outbound connection over the SOCKS server. The Rbind() inbound connection
must be from the same IP address that was used by the outbound connection that
the connect() established.

The following illustration shows a detailed overview of how socket functions


interact with a SOCKS server transparent to the application. In the example, the
FTP client calls the Rbind() function instead of a bind() function. It makes this call
by recompiling the FTP client code with the __Rbind preprocessor #define, which
defines bind() to be Rbind(). Alternatively, an application can explicitly code
Rbind() in the pertinent source code. If an application does not require inbound
connections across a SOCKS server, Rbind() should not be used.

Note: The FTP client uses Rbind(). FTP protocol allows the FTP server to establish
a data connection because of a request from the FTP client to send files or

14 Socket programming
data.

1 FTP client initiates an outbound TCP connection to a less secure network
through a SOCKS server. The destination address that the FTP client
specifies on the connect() is the IP address and port of the FTP server
located on the less secure network. The secure host system is configured
through the SOCKS page to direct this connection through the SOCKS
server. When configured, the system automatically directs the connection to
the SOCKS server that was specified through the SOCKS page.

Chapter 3. How do sockets work? 15


2 A socket is opened and Rbind() is called to establish an inbound TCP
connection. When established, this inbound connection is from the same
destination-outbound IP address that was specified above. You must pair
outbound and inbound connections over the SOCKS server for a particular
thread. In other words, all Rbind() inbound connections should
immediately follow the outbound connection over the SOCKS server. You
cannot attempt to intervene non-SOCKS connections relating to this thread
before the Rbind() runs.
3 getsockname() returns the SOCKS server address. The socket logically
binds to the SOCKS server IP address coupled with a port that is selected
through the SOCKS server. In this example, the address is sent through the
″control connection″ Socket CTLed to the FTP server that is located on the
less secure network. This is the address to which the FTP server connects.
The FTP server connects to the SOCKS server and not directly to the secure
host.
4 The SOCKS server establishes a data connection with the FTP client and
relays data between the FTP client and the FTP server. Many SOCKS
servers allow a fixed length of time for the server to connect to the Secure
client. If the server does not connect within this time, errno
ECONNABORTED is encountered on the accept().

AnyNet support for sockets


AS/400 sockets over Systems Network Architecture (SNA) are also a part of IBM’s
AnyNet implementation for the Multiprotocol Transport Networking (MPTN)
architecture. MPTN architecture provides for the following:
v The ability to run application programs that were designed to operate on one
transport network over additional transport networks. One example is Advanced
Program-to-Program Communications (APPC) programs that run over an IP
network.
v The ability to connect application programs across transport networks of
different types.

AnyNet is a family of products that carry out portions of the MPTN architecture.
They allow applications that are written for one type of transport to run over a
different type of transport network.

The term AnyNet refers to the concept of having any combination of API visible
addresses and actual transport provider protocols. For example, SNA addresses
over an IP network, or IP addresses over an SNA network are combinations that
are defined by AnyNet. Prior to AnyNet, your choice of API dictated your choice
of transport, and your existing transport network limited your choice of APIs.

AnyNet/400 support offers the following multiprotocol uses:


v Internet Protocol (IP) over SNA
If you have existing sockets programs and want to run them over an SNA
network, the IP over SNA version of AnyNet is a good choice.
v SNA over IP
If you have APPC programs and you want to run them over an IP network (in
addition to an SNA network), the SNA over IP version of AnyNet is a good
choice. (You can write APPC programs to the Intersystem Communications

16 Socket programming
Function (ICF) or Common Programming Interface Communications application
programming interfaces.) For more information on SNA over IP, see the APPC

Programming book.
v IP over Internetwork Packet Exchange (IPX)
This AnyNet implementation involves sockets that have an address_family of
AF_INET and that use IP addresses and reside on a Novell protocol network.

AnyNet allows you to mix and match your choice of application programs with
your choice of networks in ways that were not previously possible. Gateways
allow sockets programs on SNA networks to communicate with sockets programs
on IP networks. The same program can run simultaneously over multiple types of
networks. For example, the same sockets program can run over both IP and SNA
networks. The choice of destination addresses determines which type of network
you use.

Applications that use the socket application programming interfaces (APIs) and
that use Internet addressing can run over TCP, SNA, and IPX networks (see the
following figure).

Chapter 3. How do sockets work? 17


18 Socket programming
Chapter 4. Socket address structure and characteristics
Sockets use the sockaddr address structure to pass and to receive addresses. This
structure does not require the socket API to recognize the addressing format.

sockaddr structure:
struct sockaddr {
u_short sa_family;
char sa_data[14];
};

sockaddr structure parts:

sockaddr consists of the following parts:


v The short integer that defines the address family (the value that is specified for
address family on the socket() call)
v Fourteen bytes that are reserved to hold the address itself

Note: The length of 14 bytes is a placeholder for the address. The address can
exceed this length. The structure is generic because it does not define the
format of the address. Each of the transport providers defines a similar
address structure, which defines the exact format for its specific
addressing requirements.

Socket parameters:

When an application creates a socket with the socket() function, it must identify
the socket by specifying these parameters:
v The address family (domain in which the socket operates)
v The socket type

Address family protocols for socket()


Address family protocols provide the network transportation of application data
from one machine to another (or from one process to another within the same
machine). The application specifies the transport provider on the protocol parameter
of the socket() function.

The address_family parameter on the socket() function specifies the address


structure that is used on socket functions. The AS/400 socket APIs support these
address families:
v AF_INET
v AF_UNIX
v AF_NS
v AF_TELEPHONY

The use of these address families, their related protocols, and examples of relevant
structures follow.

AF_INET address family:

© Copyright IBM Corp. 2000 19


In Internet Protocol terminology, the addresses for this address family are Internet
Protocol (IP) addresses. Each node on a network is known as a host and has a
unique address called an Internet address. This address is a 32-bit integer. You
express an address in the form xxx.xxx.xxx.xxx, where each field is the decimal
representation of one byte (8 bits) of the address. For example, you express the
address whose hexadecimal representation is X'82638001' as 130.99.128.1.

See Internet Addresses for more information.

Transmission control protocol (TCP) and user datagram protocol (UDP) use the
sockaddr_in address structure.
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};

The sin_family field is the address family (always AF_INET for TCP and UDP).
The sin_port field is the port number, and the sin_addr field is the Internet
address. The sin_zero field is reserved, and you must set it to hexadecimal zeros.
The <netinet/in.h> header file contains the sockaddr_in structure definition.

The following table lists the various combinations of socket types and protocols
that you can use with AF_INET sockets:
Table 1. AF_INET socket combinations
Socket type Protocol
STREAM TCP, Systems Network Architecture (SNA),
Sequenced Packet eXchange (SPX)
SEQPACKET SPX
DGRAM UDP, SNA, Internetwork Packet eXchange
(IPX)
RAW IP

You can use more than one transport provider for the AF_INET address family.
SNA, TCP/UDP, and IPX/SPX protocol layers can be active on the same socket at
the same time. The ALWANYNET (Allow ANYNET support) network attribute
allows you to select the use of a transport other than TCP/IP for AF_INET socket
applications. This network attribute can be either yes or no. The default value is
no.

If the current status (the default status) is no, the use of AF_INET over an SNA
transport is not active. To use AF_INET sockets over a TCP/IP transport, set the
ALWANYNET status to no to improve CPU utilization.

Note: The ALWANYNET network attribute also affects APPC over TCP/IP, or
APPC over IPX support. For information on the use of this attribute with
APPC over TCP/IP, see Appendix A. APPC Over TCP/IP Configuration

. For information on the use of this attribute with APPC over IPX, see
APPC over IPX Configuration-Overview .

20 Socket programming
AF_INET sockets over TCP/IP can specify a type of SOCK_RAW. This means that
the socket will communicate directly with the network layer that is known as
Internet Protocol (IP). This is the usual communication layer for the TCP or UDP
transport provider. When you use SOCK_RAW sockets, the application program
specifies any protocol between 0 and 255 (except for TCP and UDP protocols). This
protocol number then flows in the IP headers when machines are communicating
on the network. In effect, the application program is the transport provider because
it must provide for all the transport services that the UDP or the TCP transport
normally provides.

AF_UNIX address family:

The system uses this address family for communicating between two programs
that are on the same physical machine. The address is a path name to an entry that
is in a hierarchical file system.

Sockets with address family AF_UNIX use the sockaddr_un address structure:
struct sockaddr_un {
short sun_family;
char sun_path[126];
};

The sun_family field is the address family. The sun_path field is the pathname. The
<sys/un.h> header file contains the sockaddr_un address structure definition.

For the AF_UNIX address family, protocol specifications do not apply because
protocol standards are not involved. The communications mechanism between the
two processes on the same machine is specific to that machine.

AF_NS address family:

This address family uses addresses that follow Novell or Xerox NS protocol
definitions. It consists of a 4-byte network, a 6-byte host (node), and a 2-byte port
number.

Note: Although the address family is AF_NS, the 2-byte port numbers are referred
to as sockets. Do not confuse them with the customary definition of a
socket. A true socket is the communications connection (endpoint) that you
obtain when you issue a socket() or an accept() function.

OS/400 supports Novell NS but does not support Xerox NS.

Sockets with address family AF_NS use the sockaddr_ns address structure:
struct sockaddr_ns {
unsigned short sns_family;
struct ns_addr sns_addr;
char sns_zero[2];
};

The sns_family field is the address family. The sns_addr field is the address, and
the sns_zero field is unused and must be hexadecimal zeros. The <netns/ns.h>
header file contains the sockaddr_ns address structure definition.

For address family AF_NS and for the SOCK_STREAM or SOCK_SEQPACKET


socket, select the transport provider by specifying NNSPROTO_SPX. For
SOCK_DGRAM sockets, when the address family equals AF_NS, select the
transport provider by specifying NNSPROTO_IPX.

Chapter 4. Socket address structure and characteristics 21


Notes:
1. OS/400 supports Novell NS.
2. Do not specify 0 as the protocol parameter for any of the socket types with
AF_NS. This restriction occurs because Berkeley Software Distributions (BSD)
implementations use Xerox NS, and OS/400 does not support Xerox NS.
AF_TELEPHONY address family:

Telephony domain sockets (sockets that use the AF_TELEPHONY address family)
permit the user to initiate (dial) and complete (answer) telephone calls through an
attached ISDN telephone network using standard socket APIs. The sockets forming
the endpoints of a connection in this domain are really the called (passive
endpoint) and calling (active endpoint) parties of a telephone call. The
AF_TELEPHONY addresses are telephone numbers that consist of up to 40 digits
(0 - 9), which are contained in sockaddr_tel address structures.

The system supports AF_TELEPHONY sockets only as connection-oriented (type


SOCK_STREAM) sockets. Keep in mind that a connection in the telephony domain
provides no more reliability than that of the underlying telephone connection. If
guaranteed delivery is desired, you must accomplish this at the application level,
such as in fax applications that use this family.

Sockets with address family AF_TELEPHONY use the sockaddr_tel address


structure.
struct sockaddr_tel {
short stel_family;
struct tel_addr stel_addr;
char stel_zero[4];
};

The telephony address consists of a 2-byte length followed by a telephone number


of up to 40 digits (0 - 9).
struct tel_addr {
unsigned short t_len;
char t_addr[40];
};

The stel_family field is the address family. The stel_addr field is the telephony
address, and stel_zero is a reserved field. The <nettel/tel.h> header file contains
the tel_addr and sockaddr_tel structure definitions.

Type parameter for socket()


The type of socket that is used provides the connection that enables the
transportation of data from one machine to another (or from one process to
another within the same machine). AS/400 supports the following socket types:

Stream (SOCK_STREAM):

This type of socket is connection-oriented. Establish an end-to-end connection by


using the bind(), listen(), accept(), and connect() functions. SOCK_STREAM sends
data without errors or duplication, and receives the data in the sending order.
SOCK_STREAM builds flow control to avoid data overruns. It does not impose
record boundaries on the data. SOCK_STREAM considers the data to be a stream
of bytes. In the AS/400 implementation, you can use stream sockets over

22 Socket programming
Transmission Control Protocol (TCP), Systems Network Architecture (SNA),
Internetwork Packet eXchange (IPX) networks, and to communicate with systems
outside a secure host (firewall).

Datagram (SOCK_DGRAM):

In Internet Protocol terminology, the basic unit of data transfer is a datagram. This
is basically a header followed by some data.

The datagram socket is connectionless. It establishes no end-to-end connection with


the transport provider (protocol). The socket sends datagrams as independent
packets with no guarantee of delivery. You can lose or duplicate data. Datagrams
can arrive out of order. The size of the datagram is limited to the data size that
you can send in a single transaction. For some transport providers, each datagram
can use a different route through the network. You can issue a connect() function
on this type of socket. However, on the connect() function, you must specify the
destination address that the program sends to and receives from.

In the AS/400 implementation, you can use datagram sockets over user datagram
protocol (UDP), SNA, and IPX networks.

Sequenced packet (SOCK_SEQPACKET):

Establish an end-to-end connection for sequenced packet sockets. However, this


type of socket imposes a format on the data. SOCK_SEQPACKET maintains record
boundaries, and is visible to the socket application program. Support for
out-of-band data depends on the address family that is used for the socket.

In the AS/400 implementation, you can use sequenced packet sockets over
Sequenced Packet eXchange (SPX) sockets.

Raw (SOCK_RAW):

This type of socket allows direct access to lower-layer protocols, such as Internet
Protocol (IP) and Internet Control Message Protocol (ICMP). SOCK_RAW requires
more programming expertise because you manage the protocol header information
that is used by the transport provider. At this level, the transport provider may
dictate the format of the data and the semantics that are transport-provider
specific.

Chapter 4. Socket address structure and characteristics 23


24 Socket programming
Chapter 5. Setting up the socket application environment
Communication between application programs running on different systems in a
network requires more than coding the sockets functions. You must set up an
environment for the socket system functions to operate successfully. The following
figure is a general picture of how other elements support socket system functions.

Use the following information to set up the socket environment:


v Configuring the socket environment, which includes requirements and
prerequisite information
v Coding aspects of the socket environment

© Copyright IBM Corp. 2000 25


Requirements for configuring a socket environment
Configuring a network over Transmission Control Protocol/Internet Protocol
(TCP/IP) or over Systems Network Architecture (SNA) transports requires creating
standard configuration objects such as lines, controllers, and devices. AF_INET
sockets over TCP or User Datagram Protocol (UDP) require line descriptions,
controller descriptions, and device descriptions for TCP/IP. For additional

information about TCP/IP descriptions, see Configuring TCP/IP .

You need the following communications objects for AF_INET sockets over SNA.
For information on communications objects, see the IBM AnyNet Guide to APPC

over TCP/IP for Windows book:


v APPC line description
v APPC controller description
v APPC device descriptions

See IP over IPX for more information about IP over IPX configuration.

The processes needed to configure IP over SNA follow:


1. Obtain the prerequisite information needed to configure IP over SNA.
2. Configure the socket environment.
3. Debug the IP over SNA configuration.
4. (optional) Configure the SOCKS server to communicate with a secure client
host.
5. (optional) Configure the AF_TELEPHONY domain sockets.
6. (optional) Configure the UNIX domain sockets.
7. (optional) Set the out-of-band data.

Prerequisite information to configure IP over SNA


Following is a list of the prerequisite information needed to configure IP over
SNA:
1. Get an IP network identifier. This is a range of IP addresses unique to your
company. If you do not already have an IP network identifier, you can get one

from the Internet Network Information Center .


2. Put together an SNA network layout including each system that will use IP
over SNA. You may find it helpful to show the topology of the network (how
each system connects to adjacent systems in the network). For each system in
the network, obtain the following information:
v SNA network identifier
v SNA local location names (SNA local LU names)
v SNA APPN node type (if you are using APPN)
v SNA APPN control point name (if you are using APPN)
v Additional empty spaces for the addition of IP addresses in a later step
Use the Display Network Attributes (DSPNETA) and Change Network
Attributes (CHGNETA) commands to identify and define (respectively) this
information for your system. For more details and alternatives to defining an
SNA network, see the APPC, APPN, and HPR configuration examples.

26 Socket programming
You may need to add systems to your SNA network for AnyNet gateways or
nodes that you want to migrate from the IP network to AnyNet support.

Note: Systems that remain in the IP network do not need SNA names because
the system routes them through an AnyNet gateway.

Each system (or node) in the SNA network must have at least one unique SNA
address. The address is the combination of the SNA network identifier and the
SNA local location name (SNA LU name).
3. Define the subset of your IP network addresses (your IP network identifier) for
use by IP over SNA support. Make sure that you allow enough space for at
least one unique IP address for each system that is using IP over SNA. Make
sure that you allow enough space for any future additions to the network as
well.
An IP address has two parts: The network portion and the host portion. The
network portion is unique to each company, and the host portion is unique to
each system (host) in the network. Where the network portion ends and the
host portion begins is different for each class of IP address. You can determine
this by looking at the two high-order bits in the IP address.

Class and size of


network Range Network ID Host ID
Class A (Large) 1 - 127 Byte 1 Bytes 2, 3, 4
Class B (Medium) 128 - 191 Bytes 1, 2 Bytes 3, 4
Class C (Small) 192 - 223 Bytes 1, 2, 3 Byte 4

The IP network portion can represent a very large network that spans multiple
geographic sites. To make this situation easier to manage, you can use
subnetworks. Subnetworks use the two parts of the address to define a set of IP
addresses that are treated as group. A subnetwork can be used, for example, to
define which addresses should be routed over the SNA network instead of the
IP network.

You configure a subnetwork by defining a mask, which is a series of bits. Then,


the system performs a logical AND operation on these bits and the IP address.
The 1 bits define the subnetwork portion of the IP address (which must include
at least the network portion). The 0 bits define the host portion.
4. Map the addresses in the IP over SNA subnet to each system that will support
IP over SNA. You need one IP address for each LU name that will send or
receive using IP over SNA support. This means at least one IP address for each
system; you need more if you want to use multiple LU names per system.

Configuring the socket environment


For specific examples of configuration, see the socket configuration examples
found in “Chapter 7. Examples: Using sockets” on page 69. The general steps to
perform configuration are as follows:
1. Designate the IP (Internet Protocol) addresses that are assigned to the local
host. (These IP addresses are normally referred to as interfaces.) Use the
following CL commands:
ADDIPSIFC
Add IP over SNA Interface

Chapter 5. Setting up the socket application environment 27


CHGIPSIFC
Change IP over SNA Interface
RMVIPSIFC
Remove IP over SNA Interface
2. Map the IP addresses to Systems Network Architecture (SNA) network ID and
SNA local LU name pairs. This requires designating the SNA location names
(LU names) that are associated with each IP address for each remote host. Use
the following CL commands:
ADDIPSLOC
Add IP over SNA Location
CHGIPSLOC
Change IP over SNA Location
RMVIPSLOC
Remove IP over SNA Location

The Add IP over SNA Location (ADDIPSLOC) command is used to map the IP
address of a particular system to its SNA network ID and SNA local LU name.

Two additional commands are available to assist in doing the IP-to-LU


mapping:
a. The Convert IP Address into Network Identifier/Location Name
(CVTIPSIFC) command determines the SNA location name from an IP
address.
b. The Convert Network Identifier/Location Name into IP Address
(CVTIPSLOC) command determines the IP address from an SNA location
name.

Note: For more information on the use of the CVTIPSIFC and CVTIPSLOC
commands, see “When to use IP-to-LU mapping” on page 66 and
“Debugging configuration for IP over SNA” on page 29.
3. Designate the IP routes to the remote hosts. The system requires these routes
whenever a remote host is not part of the same IP network. You must reach a
host that is not part of the same network in one of the following ways:
v By using a gateway
v From an IP address defined in the local host that has direct access to the
remote network
The IP route indicates the first gateway on the path to the remote host. (The
first gateway must be on the same network as the local host.) You can use the
following CL commands:
ADDIPSRTE
Add IP over SNA Route
RMVIPSRTE
Remove IP over SNA Route
4. Finally, activate the AnyNet support on your system by entering
CHGNETA ALWANYNET(*YES) on the AS/400 system.

Note: For specific information on the parameters for the commands listed for
configuration, see the CL command information.

You are now ready to run sockets programs over an SNA network!

28 Socket programming
Debugging configuration for IP over SNA
Use these commands to debug IP over SNA configurations:
1. The Start Mode (STRMOD) CL command can help you determine if your SNA
configuration is correct. You need the remote location name as input to the
STRMOD command. Determine the remote location name from the destination
IP address by using the Convert IP over SNA Interface (CVTIPSIFC) command.
The message you receive when STRMOD completes tells you whether it was
successful.
2. The TCP/IP FTP command can help you determine if your AnyNet
configuration is correct. If you get the User prompt, the AnyNet configuration
is correct.

Note: When FTP fails, it does not give a detailed reason for the failure. To get a
detailed reason, run a sockets program that reports the value for errno
when the failure occurs.

The following table shows common errors for an IP over SNA configuration.
Table 2. Common errors for an IP over SNA configuration
Sockets error (value of errno) Possible causes
EACCES v The user is not authorized to transport on the client
system.
v The user is not authorized to the APPC device description
that is on client system.
EADDRNOTAVAIL v AnyNet is not active on the client system (ALWANYNET
attribute is set to *NO), but TCP still started.
v The mode could not be added to the device on the client
system.
ECONNABORTED v Line error.
v Device, controller, or line varied off on the client or the
server system while still in use.
v The user is not authorized to an APPC device description
that is on the server system.
ECONNREFUSED v AnyNet is not active on the client system (ALWANYNET
attribute is set to *NO).
v The listen() API is not active on the server system.
EHOSTUNREACH v The ADDIPSLOC command is missing on the client
system.
v The ADDIPSIFC command is missing on the client
system.
v The type of service points to a nonexistent mode
description that is on the client system.
v The ADDIPSLOC command that is on the client system
failed to find a location name.
v The ADDIPSLOC command that is on the client system
found a location name that is on a non-APPC device
description.

Chapter 5. Setting up the socket application environment 29


Table 2. Common errors for an IP over SNA configuration (continued)
Sockets error (value of errno) Possible causes
ETIMEDOUT v The ADDIPSLOC command that is on the client system
points to a location name that does not exist, or is on a
system that is not responding in the APPN network.
v The messages (especially inquiry messages) on message
queue QSYSOPR are waiting for a reply.
EUNATCH AnyNet is not active on the client system (ALWANYNET
attribute is set to *NO), and TCP did not start.

Configuring AS/400 client SOCKS support


You must perform two actions on the secure client host to use a SOCKS server:
1. Configuration of the SOCKS server. For instructions on how to configure your
SOCKS server on the AS/400 firewall, see Firewall: Getting started. You can

also go to Firewall for AS/400 in the Technical Studio for additional tips
and concepts. AS/400 client SOCKS support operates with any SOCKS server
that supports Version 4 SOCKS protocols.
2. On the secure client system, define all outbound client TCP connections that are
directed to the SOCKS server on the client system. You can define the secure
client SOCKS configuration entries by using the SOCKS tab. You will find this
tab under the AS/400 Operations Navigator function of Client Access/400 for
Windows 95/NT. The SOCKS tab has substantial help on configuring the
Secure client system for AS/400 client SOCKS support.

To configure client SOCKS support, do the following:


1. In Operations Navigator, expand your AS/400 server —> Network —>
Protocols —> TCP/IP.
2. Right-click TCPIP.
3. Click Properties.
4. Click the SOCKS tab.
5. Enter your connection information on the SOCKS page.

Note: The system saves the secure client SOCKS configuration data in the
QASOSCFG file of the QUSRSYS library on the secure client host system.

For more information on AS/400 client SOCKS support, see “Using AS/400 client
SOCKS support to communicate outside a firewall” on page 12.

Configuring AF_TELEPHONY sockets to telephony domain


sockets
You must configure the telephony network device and associate a telephony
domain socket with a network device (a telephone) before making or accepting
calls. To configure an application that uses the telephony domain sockets, complete
the following steps:
1. Create an ISDN network interface that describes the user interface.
Use the CRTNWIISDN command. See the following figure for an example of
one way to create the interface. Refer to the appropriate ISDN manual for
additional information.

30 Socket programming
Command Entry RCHASD4M
Request level: 4
Previous commands and messages:
> CRTNWIISDN NWID(TELE01) RSRCNAME(BRI001) ONLINE(*NO) NETTYPE (*NORTHAM) CH
LENTRY((1 *SWT)) PCLENTRY((*PPP *LOAD 1)) SPID((1 015551598)) X31NFYCLS(*
NONE) SETUPDIF(*NOLLCIE) X31DIF(*NONE) ACTTMR(16) REACTTMR(*NONE)
Network interface description TELE01 created.

Bottom
Type command, press Enter.
===>
_________________________________________________________________________________
_________________________________________________________________________________
________________________________________________________________________________
F3=Exit F4=Prompt F9=Retrieve F10=Include detailed messages
F11=Display full F12=Cancel F13=Information Assistant F24=More keys

Figure 1. Creating the ISDN network interface

2. Create outbound connection lists.


This connection list describes, through its connection list entries, telephone
numbers to use in making calls. Use the CRTCNNL and ADDCNNLE
commands for this step. See the next figure for an example.
3. Create inbound connection lists.
This connection list describes, through its connection list entries, telephone
numbers to use in accepting calls. Use the CRTCNNL and ADDCNNLE
commands for this step. Refer to the following figure for an example.

Command Entry RCHASD4M


Request level: 4
Previous commands and messages:
> CRTCNNL CNNL(TELEOUT) NETTYPE(*NORTHAM) RMVCHR(*NETTYPE)
Connection list TELEOUT created.
> ADDCNNLE CNNL(TELEOUT) ENTRY(TELEANY) RMTNBR(*ANY) INFTRFTYPE(*FAXMODEM)
Entry TELEANY added to connection list TELEOUT.
> ADDCNNLE CNNL(TELEOUT) ENTRY(TELEFAX1) RMTNBR(918885551212) INFTRFTYPE(*F
AXMODEM)
Entry TELEFAX1 added to connection list TELEOUT.
> CRTCNNL CNNL(TELEIN) NETTYPE(*NORTHAM) RMVCHR(*NETTYPE)
Connection list TELEIN created.
> ADDCNNLE CNNL(TELEIN) ENTRY(TELANY) RMTNBR(*ANY) INFTRFTYPE(*FAXMODEM)
Entry TELANY added to connection list TELEIN.

Bottom
Type command, press Enter.
===>
_____________________________________________________________________________
_____________________________________________________________________________
___________________________________________________________________________
F3=Exit F4=Prompt F9=Retrieve F10=Include detailed messages
F11=Display full F12=Cancel F13=Information Assistant F24=More keys

Figure 2. Creating the outbound connection list

4. Create the network telephony device.

Chapter 5. Setting up the socket application environment 31


Use the CRTDEVNET command, as illustrated in the next figure.
5. Create the network controller and associate it with the device.
Use the CRTCTLNET command, as illustrated in the next figure.
6. Create the PPP line and associate it with the ISDN network interface, the
connection lists, and the controller. Use the CRTLINPPP command, as
illustrated in the following figure.

Command Entry RCHASD4M


Request level: 4
Previous commands and messages:
> CRTDEVNET DEVD(TELEDEV) TYPE(*TEL) ONLINE(*NO)
Description for device TELEDEV created.
> CRTCTLNET CTLD(TELECTL) ONLINE(*NO) DEV(TELEDEV)
Description for controller TELECTL created.
> CRTLINPPP LIND(TELELIN) RSRCNAME(*NWID) SWTNWILST((TELE01 *B*CALC)) CNNL
STOUT(TELEOUT) CNNLSTOUTE(TELEANY) CNNLSTIN(TELEIN) NETCTL(TELECTL)
Line description TELELIN created.

Bottom
Type command, press Enter.
===>
____________________________________________________________________________
____________________________________________________________________________
_________________________________________________________________________
F3=Exit F4=Prompt F9=Retrieve F10=Include detailed messages
F11=Display full F12=Cancel F13=Information Assistant F24=More keys

Figure 3. Creating the device, controller, and line

7. Vary on the network interface, PPP line, controller, and device.


8. Resolve the device name to a system pointer, and use this pointer as input for
the SIOCSTELRSC command.

When the SIOCSTELRSC request completes, the device is activated and associated
with the job that issued the command. A WRKCFGSTS CFGTYPE(*LIN)
CFGD(TELTLIN) command, where TELTLIN is the PPP line name, displays the
socket connection:

32 Socket programming
Work with Configuration Status RCHASD4M
11/06/98 16:45:46
Position to . . . . . __________ Starting characters

Type options, press Enter.


1=Vary on 2=Vary off 5=Work with job 8=Work with description
9=Display mode status 13=Work with APPN status...

Opt: Description: Status: -------------Job--------------


___ TELTLIN CONNECT PENDING
___ TELTCTL ACTIVE
___ TELTDEV ACTIVE QPADEV0003 TELUSE 031395

Bottom
Parameters or command
===> ___________________________________________________________________________________
F3=Exit F4=Prompt F12=Cancel F23=More options F24=More keys

Figure 4. WRKCFGSTS display after running SIOCSTELRSC request for device

The device will remain associated with the socket until the socket closes. Finally,
you can associate more than one device with a socket. This multiple association
permits an application to listen for and answer calls on more than one device
through a single socket.

Configuring UNIX domain sockets


UNIX domain sockets (sockets that use the AF_UNIX address family) can be
connection-oriented (type SOCK_STREAM) or they can be connectionless (type
SOCK_DGRAM). Both types can be reliable because no external communication
functions connect the two processes. On connectionless sockets, however, the
system discards packets if it receives data so fast that the system runs out of
buffers.

To create a UNIX domain socket, a sockets program specifies AF_UNIX for the
address family when it calls a socket() or socketpair() function. The semantics and
functions available to the sockets programmer are the same as when using
AF_INET sockets.

The name space for UNIX domain sockets consists of path names. The sockets
program calls the bind() function, and creates an entry in the file system directory.
If the path name already exists, the bind() fails. Thus, a UNIX domain socket
program should always call an unlink() function to remove the directory entry
when it ends.

Note: Path names in the file system are NLS-enabled.

AF_UNIX is the only address family to support the socketpair() function. The
socketpair() function returns two unnamed and connected socket descriptors.

UNIX domain datagram sockets act differently than UDP datagram sockets. With
UDP datagram sockets, the client program does not have to call the bind()
function because the system assigns an unused port number automatically. The

Chapter 5. Setting up the socket application environment 33


server can then send a datagram back to that port number. However, with UNIX
domain datagram sockets, the system does not automatically assign a path name
for the client. Thus, all client programs that use UNIX domain datagrams must call
the bind() function.

Note: The exact path name specified on the client’s bind() is what is passed to the
server. Thus, if the client specifies a relative path name, the server cannot
send the client a datagram unless it is running with the same current
directory. A relative path name is a path that is not listed by starting with
slash (/).

An example path name that an application might use for this address family is
/tmp/myserver or servers/thatserver. With servers/thatserver, you have a path
name that is not fully qualified (no / was specified). This means that the location of
the entry in the file system hierarchy should be determined relative to the current
working directory.

Setting the out-of-band data for sockets


Out-of-band (OOB) data is user-specific data that only has meaning for
connection-oriented (stream) sockets. The system generally receives stream data in
the same order sent. OOB data is received independent of its position in the stream
(independent of the order in which it was sent). This occurs because the data is
marked in such a way that, when it is sent from program A to program B, program
B is notified of its arrival.

Only sockets that have an address family of AF_INET and a type of


SOCK_STREAM support OOB data.

You send OOB data by specifying the MSG_OOB flag on the send(), sendto(), and
sendmsg() functions.

The transmission of OOB data is the same as the transmission of regular data. It is
sent after any data that is buffered. In other words, OOB data does not take
precedence over any nonbuffered data. The system transmits the data in the order
sent.

On the receiving side, things are a little more complex:


v The sockets API keeps track of OOB data that is received on a system by using
an OOB marker. The OOB marker points to the last byte in the OOB data that
was sent.

Note: The value that indicates which byte the OOB marker points to is set on a
system basis (all applications use that value). (For information on using
the Change TCP Attributes (CHGTCPA) command to change which byte
this marker points to, see the alphabetical listing of commands. This value
must be consistent between the local and remote ends of a TCP
connection. Socket applications that use this value must use it consistently
between the client and server applications.

The ioctl() SIOCATMARK request determines if the read pointer is pointing to


the last OOB byte.

Note: If you send multiple occurrences of OOB data, the OOB marker points to
the last OOB byte of the final OOB data occurrence.

34 Socket programming
v Independent of whether you receive OOB data inline or not, an input operation
processes data up to the OOB marker, if the system sent OOB data.
v You can use a recv(), recvmsg(), or recvfrom() function (with the MSG_OOB flag
set) to receive OOB data. An error of [EINVAL] is returned if one of the receive
functions has completed and one of the following occurs:
– You have not set the socket option SO_OOBINLINE , and there is no OOB
data to receive.
– You have set the socket option SO_OOBINLINE.
Set the socket option SO_OOBINLINE; otherwise, when OOB data sent is greater
than one byte, the system considers all the bytes except for the last byte as
normal data. (Normal data means that the receiving program can receive data
without specifying the MSG_OOB flag.) The last byte of the OOB data that was
sent is not stored in the normal data stream. You can retrieve this byte only by
issuing a recv(), recvmsg(), or recvfrom() function with the MSG_OOB flag set.
If a receive function is issued with the MSG_OOB flag not set and normal data
is received, the OOB byte is deleted. Also, if the system sends multiple
occurrences of OOB data, the OOB data from the preceding occurrence is lost.
Furthermore, the position of the OOB data for the final OOB data occurrence is
remembered.

If you set the socket option SO_OOBINLINE, all of the OOB data sent is stored
in the normal data stream. You can retrieve data by issuing one of the three
receive functions without specifying the MSG_OOB flag. (If it is specified, the
system returns an error of [EINVAL]). OOB data is not lost if you send multiple
occurrences of OOB data:
v The system does not discard the OOB data if you did not set SO_OOBINLINE,
but received OOB data, and then set SO_OOBINLINE on. The system considers
the initial OOB byte normal data.
v If you did not set SO_OOBINLINE, but you sent OOB data, and the receiving
program issued an input function to receive the OOB data, then the OOB marker
is still valid. The receiving program can still determine whether the read pointer
is at the OOB marker, even though it received the OOB byte.

Chapter 5. Setting up the socket application environment 35


The following two figures show what happens when you receive OOB data.

36 Socket programming
Coding aspects of the socket environment
Here are some coding considerations to help you set up the socket environment:
v Find and install the socket header files for use with the socket function.
v Understand SSL programming protocols to provide communications privacy
over the Internet.
v Use sendmsg() and recvmsg() APIs to pass descriptors to design simpler logic
for server and worker jobs.
v Use the send_file() and accept_and_recv() APIs to transfer files over connected
sockets faster and easier.

Socket header files


Part of the socket APIs consist of structures and defined macros that are shipped as
header files (files that you can include in a user program). Programs that use the
socket functions must include one or more header files that contain information
that is needed by the functions, such as:

Chapter 5. Setting up the socket application environment 37


v Macro definitions
v Data type definitions
v Structure definitions
v Function prototypes

You can find the header files in the QSYSINC library, which you can optionally
install. Make sure that QSYSINC is on your system before you compile programs
that use these header files.

To install the QSYSINC library:


1. Load the media that contains the OS/400 Openness Includes. The OS/400
Openness Includes are packaged as part of OS/400, and contain the header
files.
2. On an OS/400 command line, enter GO LICPGM.
3. On the resulting Work with Licensed Programs menu, select the Install
licensed programs option.
4. On the Install Licensed Programs display, select the OS/400 Openness Includes
option.

The syntax of each socket function lists the header files required for that function
(see the socket API information). For example, the format for accept() lists the
following header files.
#include <sys/types.h>
#include <sys/socket.h>

The application programmer must include <sys/socket.h> and <sys/types.h> in the


program for the accept() function to run successfully.

The following table lists the header files used by socket functions. The Description
column states the purpose of the header file and gives the file name and member
name of the header file in QSYSINC.
Table 3. Header files for the sockets APIs
File name Description
<arpa/inet.h> Defines prototypes for those network library routines that
convert Internet address and dotted-decimal notation, for
example, inet_makeaddr().
File: ARPA
Member:
INET

<arpa/nameser.h> Defines Internet name server macros and structures that are
needed when the system uses the resolver routines.
File: ARPA
Member:
NAMESER

38 Socket programming
Table 3. Header files for the sockets APIs (continued)
File name Description
<error.h> Defines macros and variables for error reporting.
File: H
Member:
ERRNO
Note: This file also exists as file H, member ERRNO in
library QCLE.
<fcntl.h> Defines prototypes, macros, variables, and structures for
control-type functions, for example, fcntl().
File: H
Member:
FCNTL

<net/if.h> Defines prototypes, macros, variables, and the ifreq and


ifconf structures that are associated with ioctl() requests that
affect interfaces.
File: NET
Member:
IF

<net/route.h> Defines prototypes, macros, variables, and the rtentry and


rtconf structures that are associated with ioctl() requests that
affect routing entries.
File: H
Member:
ROUTE

<netdb.h> Contains data definitions for the network library routines.


Defines the following structures:
v hostent and hostent_data
v netent and netent_data
v servent and servent_data
v protoent and protoent_data
File: H
Member:
NETDB

<netinet/in.h> Defines prototypes, macros, variables, and the sockaddr_in


structure to use with Internet domain sockets.
File: NETINET
Member:
IN

Chapter 5. Setting up the socket application environment 39


Table 3. Header files for the sockets APIs (continued)
File name Description
<netinet/ip.h> Defines macros, variables, and structures that are associated
with setting IP options.
File: NETINET
Member:
IP

<netinet/ip_icmp.h> Defines macros, variables, and structures that are associated


with the Internet Control Message Protocol.
File: NETINET
Member:
IP_ICMP

<netinet/tcp.h> Defines macros, variables, and structures that are associated


with setting TCP options.
File: NETINET
Member:
TCP

<netns/idp.h> Defines IPX packet header. May be needed in AF_NS socket


applications.
File: NETNS
Member:
IDP

<netns/ipx.h> Defines ioctl structures for IPX ioctl() requests. May be


needed in AF_NS socket applications.
File: NETNS
Member:
IPX

<netns/ns.h> Defines AF_NS socket structures and options. You must


include this file in AF_NS socket applications.
File: NETNS
Member:
NS

<netns/sp.h> Defines SPX packet header. May be needed in AF_NS socket


applications.
File: NETNS
Member:
SP

40 Socket programming
Table 3. Header files for the sockets APIs (continued)
File name Description
<nettel/tel.h> Defines sockaddr_tel structure and related structures and
macros. You must include this file in AF_TELEPHONY
socket applications.
File: NETTEL
Member:
TEL

<resolv.h> Contains macros and structures that are used by the resolver
routines.
File: H
Member:
RESOLV

<ssl.h> Defines Secure Sockets Layer (SSL) prototypes, macros,


variables, and the following structures:
v SSLInit
v SSLHandle
File: H
Member:
SSL

<sys/ioctl.h> Defines prototypes, macros, variables, and structures for I/O


control-type functions, for example, ioctl().
File: SYS
Member:
IOCTL

<sys/param.h> Defines some limits to system fields, in addition to


miscellaneous macros and prototypes.
File: H
Member:
PARAM

<sys/signal.h> Defines additional macros, types, structures, and functions


that are used by signal routines.
File: SYS
Member:
SIGNAL

Chapter 5. Setting up the socket application environment 41


Table 3. Header files for the sockets APIs (continued)
File name Description
<sys/socket.h> Defines socket prototypes, macros, variables, and the
following structures:
v sockaddr
v msghdr
v linger

You must include this file in all socket applications.


File: SYS
Member:
SOCKET

<sys/time.h> Defines prototypes, macros, variables, and structures that are


associated with time functions.
File: SYS
Member:
TIME

<sys/types.h> Defines various data types. Also includes prototypes,


macros, variables, and structures that are associated with the
select() function. You must include this file in all socket
applications.
File: SYS
Member:
TYPES

<sys/uio.h> Defines prototypes, macros, variables, and structures that are


associated with I/O functions.
File: SYS
Member:
UIO

<sys/un.h> Defines prototypes, macros, variables, and the sockaddr_un


structure to use with UNIX domain sockets.
File: SYS
Member:
UN

<unistd.h> Contains macros and structures that are defined by the


integrated file system. Needed when the system uses the
read() and write() system functions.
File: H
Member:
UNISTD

42 Socket programming
SSL programming protocols
The Secure Sockets Layer (SSL) is a popular set of security protocols that were
originally developed by Netscape Communications Corporation. SSL provides
communications privacy over the Internet. The protocol allows client/server
applications to communicate in a way that is designed to prevent eavesdropping,
tampering, and message forgery.

The SSL protocol is a layered protocol. This protocol is used on top of a reliable
transport such as Transmission Control Protocol (TCP) to provide secure
communications for an application. Application protocols that require secure
communications are HTTP, FTP, SMTP, and Telnet, among others.

OS/400 provides a set of SSL APIs. The SSL APIs allow a client/server application
that is written using sockets to use SSL protocol for secure communication. An
application that uses SSL for secure communications basically is a client/server
application that is written using sockets. The OS/400 SSL APIs support server and
client authentication.

An SSL-enabled application usually needs to use a different port than an


application that is not SSL-enabled. For example, an SSL-enabled browser accesses
an SSL-enabled Hypertext Transfer Protocol (HTTP) server with a Universal
Resource Locator (URL) that begins HTTPS rather than HTTP. In most cases, a
URL of HTTPS attempts to open a connection to port 443 of the server system.
(The standard HTTP server uses port 80).

There are multiple versions of the SSL protocol defined. The OS/400
implementation supports SSL Version 3.0, SSL Version 2.0, and SSL Version 3.0
with 2.0 compatibility. The OS/400 implementation does not support a new

Request for Comment (RFC) that is progressing through the Internet

Engineering Task Force (IETF) RFC process to become a standard. This RFC
defines Transport Layer Security (TLS). The TLS protocol is based heavily on the
SSL Version 3.0 protocol, but they are not identical.

The SSL APIs in the OS/400 implementation follow:


Table 4. Secure Sockets Layer APIs
Function Description
SSL_Create() Enable SSL support for the specified socket
descriptor
SSL_Destroy() End SSL support for the specified SSL
session
SSL_Handshake() Initiate the SSL handshake protocol
SSL_Init() Initialize the current job for SSL
SSL_Read() Receive data from an SSL-enabled socket
descriptor
SSL_Write() Write data to an SSL-enabled socket
descriptor

If you plan to use the SSL APIs to create secure-enabled applications, you must
first obtain and install one of the following licensed programs:
v Cryptographic Access Provider 40–Bit (5769-AC1)

Chapter 5. Setting up the socket application environment 43


v Cryptographic Access Provider 56–Bit (5769-AC2)
v Cryptographic Access Provider 128–Bit (5769-AC3)

You must also install the OS/400 Openness Includes (5769SS1). The SSL APIs use
header (include) files from the library QSYSINC, which you can install as part of
the OS/400 System Openness Includes option.

The ILE C language programs provide the use of the SSL APIs. When you create
ILE programs or service programs that use the OS/400 SSL APIs, you do not need
to explicitly bind to the SSL service program, QSYS/QSOSSLSR. Because the SSL
service program is part of the system binding directory, it automatically binds to
the SSL service program.

The SSL APIs require some cryptographic objects. This documentation does not
contain information about configuring or obtaining cryptographic objects, such as
key ring files or certificates, which are required in order to use the SSL APIs. Some
cryptographic objects, such as key ring files, are required parameters for SSL APIs.
See Secure Sockets Layer (SSL) APIs for more information.

An application that uses the sockets and SSL APIs contains the following elements:
v A call to socket() to obtain a socket descriptor.
v A call to SSL_Init() to initialize the job environment for SSL processing. An
SSL_Init() call must succeed at least once in a job.
v Socket calls to activate a connection. It calls connect() to activate a connection for
a client program, or it calls bind(), listen(), and accept() to activate a connection
for a server program.
v A call to SSL_Create() to enable SSL support for the connected socket.
v A call to SSL_Handshake() to initiate the SSL handshake negotiation of the
cryptographic parameters.

Note: Both a server program and the client programs with which it
communicates must provide a certificate for an SSL handshake to succeed.
A server must also provide the private key that is associated with its
certificate or its key ring file. The SSL_Init() call identifies the key ring
file from which the certificate and private key are obtained for all SSL
sessions established for a job.
v Calls to SSL_Read() and SSL_Write() to receive and send data.
v A call to SSL_Destroy() to disable SSL support for the socket.
v A call to close() to destroy the connected sockets.

Detailed information about each of the OS/400 SSL APIs is available in the SSL
API information.

Using sendmsg() and recvmsg() to pass descriptors


The ability to pass an open descriptor between jobs can lead to a new way of
designing client and server applications. Passing an open descriptor between jobs
allows one process, typically a server, to do the following:
1. Do everything that is required to obtain the descriptor (open a file, establish a
connection, wait for the accept() API to complete)
2. Allow another process, typically a worker, to handle all the data transfer
operations when the descriptor is open

44 Socket programming
This design results in simpler logic for both the server and the worker jobs. This
design also allows easy support for different types of worker jobs. The server can
make a simple check to determine which type of worker should receive the
descriptor.

AS/400 provides these sets of APIs that can pass descriptors between AS/400 jobs:
v spawn()
v givedescriptor() and takedescriptor()
v sendmsg() and recvmsg()

The spawn() API starts a new AS/400 job (often called a ″child job″) and gives
certain descriptors to that child job. If the child job is already active, use the
givedescriptor() and takedescriptor() APIs, or the sendmsg() and recvmsg() APIs.

The sendmsg() and recvmsg() APIs offer many advantages over the
givedescriptor() and takedescriptor() APIs:
v Portability
The givedescriptor() and takedescriptor() APIs are nonstandard and unique to
AS/400. If the portability of an application between AS/400 and UNIX is
important, you may want to use the sendmsg() and recvmsg() APIs instead.
v Communication of control information
Often the worker job needs to know additional information when it receives a
descriptor, such as the following:
– What type of descriptor is it?
– What should the worker job do with it?
The sendmsg() and recvmsg() APIs allow you to transfer data, which may be
control information, along with the descriptor; the givedescriptor() and
takedescriptor() APIs do not.
v Performance
Applications that use the sendmsg() and recvmsg() APIs tend to perform
slightly better than those that use the givedescriptor() and takedescriptor() APIs
in these areas:
– Elapsed time
– CPU utilization
– Scalability
The amount of performance improvement for an application depends on the
extent that the application passes descriptors.
v Pool of worker jobs
You may want to set up a pool of worker jobs so that a server can pass a
descriptor and only one of the jobs in the pool receives it. You can use the
sendmsg() and recvmsg() APIs to accomplish this by having all of the worker
jobs wait on a shared descriptor. When the server calls sendmsg(), only one of
the worker jobs receives the descriptor.
v Unknown worker job ID
The givedescriptor() API requires the server job to know the job identifier of the
worker job. Typically the worker job obtains the job identifier and transfers it
over to the server job with a data queue. The sendmsg() and recvmsg() do not
require the extra overhead to create and manage this data queue.
v Adaptive server design

Chapter 5. Setting up the socket application environment 45


When a server is designed by using the givedescriptor() and takedescriptor(),
you typically use a data queue to transfer the job identifiers from worker jobs
over to the server. The server then does a socket(), bind(), listen(), and an
accept(). When the accept() API completes, the server pulls off the next available
job ID from the data queue. It then passes the inbound connection to that
worker job. Problems arise when many incoming connection requests are
occuring at once and not enough worker jobs are available. If the data queue
that contains the worker job identifiers is empty, the server blocks waiting for a
worker job to become available, or the server creates additional worker jobs. In
many environments, neither of these alternatives are desirable because
additional incoming requests may fill the listen backlog.
Servers that use sendmsg() and recvmsg() APIs to pass descriptors remain
unhindered during heavy activity. They do not need to know which worker job
is going to handle each incoming connection. When a server calls sendmsg(), the
descriptor for the incoming connection and any control data are put in an
internal queue for the AF_UNIX socket. When a worker job becomes available, it
calls recvmsg() and receives the first descriptor and the control data that was in
the queue.
v Inactive worker job
The givedescriptor() API requires the worker job to be active while the
sendmsg() API does not. The job that calls sendmsg() does not require any
information about the worker job. The sendmsg() API requires only that you set
up an AF_UNIX socket connection.

An example of how you can use the sendmsg() API to pass a descriptor to a job
that does not exist follows.

A server can do the following:


1. Use the socketpair() API to create a pair of AF_UNIX sockets.
2. Use the sendmsg() API to send a descriptor over one of the AF_UNIX sockets
created by socketpair().
3. Call spawn() to create a child job that inherits the other end of the socket pair.
The child job calls recvmsg() to receive the descriptor that the server passed. The
child job was not active when the server called sendmsg().

Pass more than one descriptor at a time: The givedescriptor() and takedescriptor()
APIs allow only one descriptor to pass at a time. You can use the sendmsg() and
recvmsg() APIs to pass an array of descriptors.

Using send_file() and accept_and_recv() APIs


These socket APIs enable faster and easier file transfers over connected sockets.
The send_file() and accept_and_recv() APIs are especially useful for file-serving
applications such as Hypertext Transfer Protocol (HTTP) servers.

The send_file() verb enables the sending of file data directly from a file system
over a connected socket with a single API call.

The accept_and_recv() is a combination of three socket functions: accept(),


getsockname(), and recv().

See “Examples: Establishing communications using the send_file() and


accept_and_recv() APIs” on page 136 for the examples concerning the

46 Socket programming
communications configuration that use the send_file() and accept_and_recv() APIs.
See the Socket APIs for additional information on the send_file() and
accept_and_recv() APIs.

Chapter 5. Setting up the socket application environment 47


48 Socket programming
Chapter 6. Improving performance of the socket environment
You can enhance the performance of the socket environment in several ways. The
following topics provide considerations in planning a socket environment. See the
topics listed below for specific details:
v “Socket network functions”
v “Optimal setting of the listen() back_log parameter” on page 51
v “sendmsg() and recvmsg() API considerations” on page 51
v “Using sockets to signal applications” on page 55
v “Returning blocked socket functions” on page 56
v “Using select() for I/O multiplexing” on page 57
v “Major components of DNS for sockets” on page 57
v “Format of DNS queries and responses for sockets” on page 57
v “When to use IP-to-LU mapping” on page 66

Socket network functions


Socket network functions allow application programs to obtain information from
the host, protocol, service, and network files. You can access the information by
name, by address, or by sequential processing of the file.

Setting up communications between programs that run across networks requires


these network functions (or routines). AF_UNIX sockets are not used because they
require the communicating programs to exist on the same physical machine.

The routines do the following:


v Map host names to network addresses
v Map network names to network numbers
v Map protocol names to protocol numbers
v Map service names to port numbers
v Convert the byte order of Internet network addresses
v Convert Internet address and dotted decimal notation

Included in the network routines is a group of routines called resolver routines.


These routines make, send, and interpret packets for name servers in the Internet
domain, and do name resolution. The resolver routines normally get called by
gethostbyname() and gethostbyaddr(), but you can call them directly.

For reference information on the network routines, see the Socket APIs. For more
information about the files that support the network functions, see the Network
function files information.

Network function files:

The use of the AF_INET sockets depends on the following AS/400 database files:

QUSRSYS File contents


QATOCHOST List of host names and the corresponding IP addresses.

© Copyright IBM Corp. 2000 49


QUSRSYS File contents
QATOCPP List of protocols that are used in the Internet.
QATOCPS List of services, and the specific port and protocol that a service
uses.
QATOCPN List of networks and the corresponding IP addresses.
QASOSCFG Detailed network configuration information that describes how to
correctly access specific systems or networks through a firewall.

The database files above serve the same purpose as the following files found on
some other systems:
/etc/hosts
/etc/protocols
/etc/services
/etc/networks
/etc/socks.conf

The network function files contain useful network information that is used by the
socket network functions. A set of network functions that affect hosts, protocols,
services, and networks uses each file, respectively. For example, the following
functions use the hosts file:
endhostent() and endhostent_r()
gethostbyaddr() and gethostbyaddr_r()
gethostbyname() and gethostbyname_r()
gethostent() and gethostent_r()
sethostent() and sethostent_r()
See the Socket APIs for more information on the above functions.

The protocols and services files come preloaded with standard information. The
protocols file contains a list of valid protocols that are used in the Internet. The
services file contains a mapping of services to ports. (There is a limited number of
UDP and TCP port numbers that are reserved for well-known services, such as File
Transfer Protocol (FTP).) You must load the hosts and networks files with the host
names and networks and their corresponding addresses.

All of these files may require maintenance to keep their contents up to date. The
user interface for managing these files consists of CL commands and Work with
displays.

You can use the following CL commands to access the host database file:
v ADDTCPHTE (Add TCP/IP Host Table Entry)
v RMVTCPHTE (Remove TCP/IP Host Table Entry)
v CHGTCPHTE (Change TCP/IP Host Table Entry)
v RNMTCPHTE (Rename TCP/IP Host Table Entry)
v MRGTCPHT (Merge TCP/IP Host Tables)

For information on configuring host table information with these commands, see

the TCP/IP Configuration and Reference book.

50 Socket programming
You can use the following CL commands to access the network database file:
v WRKNETTBLE (Work with Network Table Entries)
v ADDNETTBLE (Add Network Table Entry)
v RMVNETTBLE (Remove Network Table Entry)

For specific information on the protocol, service, and network table commands and

their parameters, see the CL Programming book.

Note: You can obtain host data either from the Domain Name System or the
OS/400 host database file. If one does not have the data, then the other is
checked. The setting of the TCP/IP configuration switch determines which
host file is checked first. (You can access the configuration switch by using
option 12 (Change TCP/IP domain information) on the Configure TCP/IP
(CFGTCP) menu.)

Installation exit programs determine if the files exist. If the required files do not
exist, the system creates them. In addition, the system loads the service files and
protocol files with entries at first use.

Optimal setting of the listen() back_log parameter


The optimal setting of the listen backlog value depends on the following factors:
v The manner in which the server processes connection requests. Does it handle
each connection request itself, or does it pass the actual processing of the
connection to a child or worker job? The manner used to handle each connection
affects the time that the server takes to handle one connection before it can
handle the next one. The shorter the time it requires, the smaller you can set the
backlog.
v The number and rate of connection requests the server can expect over a given
period of time. This helps determine the listen backlog value. More connection
requests coming in over a shorter period of time require a larger backlog value.
v The system processor size. General machine performance and the manner in
which storage pools that are used by the server are allocated may determine
how the server performs. Server performance affects how long it takes to accept
a connection request. The faster the server performs, the smaller the backlog
value can be.

To help you determine how much main storage a connection request in the listen
back_log parameter uses, consider the following:
v Each connection request in the backlog uses at least 1KB of storage.
v Each connection request can use an additional storage amount equal to the size
of the TCP receiving buffer. You can determine the TCP receive buffer size by
looking at the TCPRCVBUF parameter value on the Change TCP Attributes
(CHGTCPA) CL command. This storage amount is used only if the remote peer
(client) sends data after the connection is established and put in the listen
backlog.

sendmsg() and recvmsg() API considerations


Following are suggestions to enhance the performance of the sendmsg() and
recvmsg() APIs.

Chapter 6. Improving performance of the socket environment 51


The sendmsg() and recvmsg() APIs are extremely flexible. You can use these APIs
to send data buffers, descriptors, or both. As a result of this flexibility, passing
descriptors with the sendmsg() and recvmsg() APIs is more complex than using
the givedescriptor() and takedescriptor() APIs. There are also a number of
considerations to be aware of when you use these APIs to pass descriptors
between AS/400 jobs:
v To pass a descriptor between AS/400 jobs with the sendmsg() and recvmsg()
APIs, you must establish an AF_UNIX socket connection between the server and
the worker jobs.

Note: A worker job or a worker thread refers to a process or subprocess


(thread) that does data processing by using the socket descriptor. For
example, a worker process accesses a database file to extract and format
information for sending to the remote peer through the socket descriptor
and its associated connection. It could then receive a response or set of
data from the remote peer and update the database accordingly.

Depending on the design of the server, the worker usually does not
perform the connection ″bring-up″ or initiation. This is usually done by
the listening or server job or thread. The listening or server job usually
passes the descriptor to the worker job or thread.

A few of ways that you can establish this AF_UNIX socket are:
– Have the server job listen on an AF_UNIX socket, and have the worker job
issue connect().
– Have the server and worker jobs use unconnected AF_UNIX datagram
sockets. When you call sendmsg(), the server job must specify the AF_UNIX
address that was bound by the worker job.
– Use the socketpair() API to create a pair of connected AF_UNIX sockets. Then
you can use the spawn() API to create a child job that inherits one of the
sockets that was created by the socketpair() API.
After the server and worker jobs each have an AF_UNIX socket, the server job
can use sendmsg() to pass a descriptor. The worker job can use recvmsg() to
receive it.

The descriptor that is passed does not have to be an AF_UNIX socket.


v You can use the msg_accrights field in the message header structure to point to
an array of descriptors for sending or receiving. You can use this field only if the
address family is AF_UNIX.
v The msg_accrightslen field in the message header structure indicates how many
descriptors to send or receive. The value that is stored in this field shows how
many bytes of descriptor data are transferred. Each descriptor is 4 bytes in
length. Therefore, if you pass one descriptor, the value stored in the
msg_accrightslen field should be 4. Use this field only if the address family is
AF_UNIX. If you pass descriptors and the value in this field is less than 4, the
system returns an EINVAL errno.
v Be careful when you use a single sendmsg() call to pass descriptors and control
data over a SOCK_STREAM connection. You may have a blocked recvmsg() that
completes only part or none of the control data. You can write an application
that calls recvmsg() to handle the case in which only part of (or none of) the
control information is received. If this situation occurs, the application should
use the recvmsg() API to receive the rest of the control data that was sent. This
behavior does not occur when you pass descriptors and control data over a
SOCK_DGRAM socket.

52 Socket programming
v When you pass descriptors over a SOCK_STREAM socket, the system maintains
the data buffers and the descriptors in separate queues. This means that the
worker job can receive the data buffer, the descriptor, or both. It also means that
the worker job can receive the descriptor long after the data buffer has been
received, or vice versa. For example, the worker job could receive additional
data buffers that were sent after the descriptor was sent, but before it receives
the descriptor. This can cause some unusual behaviors, especially if the
nondescriptor data flows are intermixed with the passing of descriptors.

Note: Some systems do not maintain socket implementation descriptors and


data buffers in separate queues when you pass them over a
SOCK_STREAM socket.
v When you pass descriptors over a SOCK_DGRAM socket, the system links
together the data buffers and the descriptors from a given sendmsg() call. The
system must receive all the data buffers and descriptors from a given sendmsg()
with a single recvmsg() call by the worker job. Any data buffer or descriptor
that is not received is discarded and reclaimed by the operating system.
v The sendmsg() and recvmsg() APIs allow you to pass a descriptor with no data
buffer. When you pass a descriptor only, you must specify the length of the data
buffer as zero. The return code from sendmsg() or recvmsg() is zero because the
system did not send or receive data. There are two ways that you can specify a
data length of zero in the message header structure:
– Pass only one iov vector buffer and set the length of that iov buffer to zero:
struct msghdr msg;
struct iovec iov[1];

msg.msg_iov = iov;
msg.msg_iovlen = 1;
iov[0].iov_base = buffer;
iov[0].iov_len = 0;
– Set the msg_iov pointer to NULL and set the msg_iovlen field to zero:
struct msghdr msg;

msg.msg_iov = NULL;
msg.msg_iovlen = 0;
v The worker job that calls recvmsg() to receive a descriptor must be running with
one of two things:
– The same user profile that the server job was running when it called
sendmsg()
– *ALLOBJ special authority
The authority checks what the sendmsg() and recvmsg() APIs do when
descriptors passed are similar to those that the givedescriptor() and
takedescriptor() APIs do. The main difference is that when you use sendmsg()
and recvmsg() to pass descriptors, the worker jobs perform the authority
checking and auditing. When you use givedescriptor() and takedescriptor(), the
server job does the authority checking and auditing.

The sendmsg() and recvmsg() APIs have additional information concerning errors.
See “Error information for the sendmsg() API” and “Error information for the
recvmsg() API” on page 54 for the details.

Error information for the sendmsg() API


The following list provides assistance in determining whether the sendmsg() API is
performing properly.

Chapter 6. Improving performance of the socket environment 53


v The sendmsg() API returns with the number of bytes that were sent, or a -1 if an
error occurred. If you call sendmsg() and specify a descriptor and 10 bytes of
data, sendmsg() returns a 10 if it was successful. The system uses
msg_accrightslen field in the message header structure to indicate how many
bytes of descriptor data (four bytes for each descriptor) to send. The sendmsg()
API never updates this descriptor.
v If an error occurs when you use sendmsg() to send one or more descriptors, the
sendmsg() API returns a -1 and sets the errno variable. If the system cannot
send a descriptor, it does not send the associated data buffer.

Error information for the recvmsg() API


The following list provides assistance in determining whether the recvmsg() API is
performing properly.
v The recvmsg() API returns the number of bytes that are received, or a -1 if an
error occurred. Check the msg_accrightslen field in the message header structure
when the recvmsg() API completes to determine how many descriptors were
received.
v If the recvmsg() API receives a data buffer but no descriptor, it returns the
number of bytes of data that were received. The system ignores the error that
occurs when the recvmsg() API attempts to receive the descriptor, unless one of
the following is true:
– You specified the length of the data buffer on the recvmsg() call as zero.
– You specified the length of the data buffer on the sendmsg() call as zero. If
you used a SOCK_STREAM connection, you must receive all the previous
data that was sent to this socket prior to calling recvmsg().
v You can use the recvmsg() API to receive a descriptor and a zero length data
buffer. However, if you call recvmsg() with a zero data length buffer, it does not
block, and it returns immediately.
If the system did not send a descriptor, the recvmsg() call immediately returns
with 0 (bytes of data), and the msg_accrightslen field is 0 (no descriptor). If a
descriptor was sent but not received, the recvmsg() call immediately returns
with 0 (bytes of data) and the msg_accrightslen field is 4 (one descriptor).
v The descriptor number that recvmsg() returns in the msg_accrights field when it
completes is the next available number in the descriptor table of that job. This
number may not match the descriptor number that the server job specified in
the msg_accrights field when it called sendmsg(). These numbers are often
different.
v When you specify the MSG_PEEK flag on a recvmsg() call, the value that it
returns in the msg_accrightslen field is one of two things:
– The total number of descriptors that were sent to this socket that could be
received (SOCK_STREAM)
– The number of descriptors that were sent with the next datagram
(SOCK_DGRAM).
The value returned in the msg_accrightslen field is a multiple of four because
each descriptor is 4 bytes in length. For example, if the system sends three
descriptors, the value recvmsg() returns in the msg_accrightslen field is 12. If the
value that was passed for msg_accrightslen is less than the value that is to be
returned, the old value is unchanged. In either case, the buffer that the
msg_accrights field points to remains unchanged.
v If the server job sends five descriptors with a single sendmsg() call and the
worker job calls recvmsg() with the msg_accrightslen field set to 12 (three
descriptors), only the first three descriptors are returned to the user. If an error

54 Socket programming
occurs on any of the first three descriptors, the fourth and fifth descriptor that
were sent are not checked. If you pass the descriptors over a SOCK_STREAM
connection, an additional recvmsg() call is necessary to receive the remaining
descriptors. If you use a SOCK_DGRAM socket, the operating system reclaims
the additional descriptors.

Using sockets to signal applications


An application program can request asynchronous notification (request that the
system send a signal) when a condition that the application is interested in occurs.
There are two asynchronous signals that sockets will send to an application.
v SIGURG is a signal that is sent when you receive out-of-band (OOB) data on a
socket for which OOB data is supported. For example, you can condition a
socket with an address family of AF_INET and a type of SOCK_STREAM to
send a SIGURG signal.
v SIGIO is a signal that is sent when normal data, OOB data, error conditions, or
when most anything happens on any type of socket.

The application should ensure that it is able to handle receiving a signal before it
requests the system to send signals. You can do this by setting up signal handlers.
One way to set a signal handler is by issuing the sigaction() call.

An application requests the system to send the SIGURG signal by one of the
following methods:
v Issuing a fcntl() call and specifying a process ID or a process group ID with the
command parameter value of F_SETOWN.
v Issuing an ioctl() call and specifying the FIOSETOWN or the SIOCSPGRP value
for the request parameter.

An application requests the system to send the SIGIO signal in two steps. First it
must set the process ID or the process group ID as described above for the
SIGURG signal. This is to inform the system where the application wants the
signal delivered. Second, the application must do either of the following:
v Issue the fcntl() call and specify the F_SETFL command with the FASYNC flag.
v Issue the ioctl() call and specify the FIOASYNC value for the request parameter.
This second step requests the system to generate the SIGIO signal. Note that you
can do these steps in any order. If an application issues these requests on a
listening socket, the values set by the requests are inherited by all sockets that are
returned to the application from the accept() function. That is, newly accepted
sockets will also have the same process ID or process group ID as well as the same
information with regard to sending the SIGIO signal.

A socket can also generate synchronous signals on error conditions. Whenever an


application receives [EPIPE] as an errno on a socket function, a SIGPIPE signal is
delivered to the process that issued the operation that is receiving the errno value.
On a Berkeley Software Distributions (BSD) implementation, by default the
SIGPIPE signal ends the process that received the errno value. To remain
compatible with previous releases of the OS/400 implementation, the OS/400
implementation uses a default behavior of ignore for the SIGPIPE signal. This
ensures that there are no negative effects to the existing applications by the
addition of the signals function.

Chapter 6. Improving performance of the socket environment 55


When a signal is delivered to a process that is blocked on a sockets function, the
function returns with the [EINTR] errno value. This allows the application’s signal
handler to run. The functions for which this will occur are:
v accept()
v connect()
v read()
v readv()
v recv()
v recvfrom()
v recvmsg()
v select()
v send()
v sendmsg()
v sendto()
v write()
v writev()

Signals do not provide the application program with a socket descriptor that
identifies where the signaled condition actually exists. If the application program is
using multiple socket descriptors, it must either poll the descriptors or use the
select() call to determine why the signal was received.

Returning blocked socket functions


When an application issues one of the socket input functions, and there is no data
to read, the function blocks and does not return until there is data to read.
Similarly, an application can block a socket output function when it cannot send
data immediately. Finally, connect() and accept() can block while waiting for
connection establishment with the target programs.

Sockets provide a method that enables application programs to issue functions that
prevent blocking so that the function returns without delay. You can do this in one
of two ways:
v Call fcntl() to turn on the O_NONBLOCK flag
v Call ioctl() to turn on the FIONBIO flag
When the socket is running in this nonblocking mode, if it cannot complete a
function without blocking, it returns immediately. A connect() may return with
[EINPROGRESS], which means that the connection initiation has started. You can
then use select() to determine when the connection has completed. For all other
functions that are affected by running in the nonblocking mode, an error code of
[EWOULDBLOCK] indicates that the call was unsuccessful.

The socket functions that can block are:


v accept()
v read()
v readv()
v recv()
v recvfrom()
v recvmsg()
v send()

56 Socket programming
v sendmsg()
v sendto()
v write()
v writev()

Using select() for I/O multiplexing


When an application wants to wait for multiple conditions at the same time, it
needs a common point at which it can wait. The select() function is that common
point. select() allows an application to specify sets of descriptors to do the
following:
v To determine whether there is data to read
v To determine whether there is data to write
v To determine whether an exception condition is present.

The descriptors that you can specify in each set can be socket descriptors, file
descriptors, or any other object that is represented by a descriptor.

The select() function also allows the application to specify if it wants to wait for
data to become available. The application can specify how long to wait.

Major components of DNS for sockets


AS/400 systems provide applications with access to the Domain Name System
(DNS) through the resolver functions. The resolvers provided in the OS/400
implementation are socket functions that provide communication with a name
server. You can use these routines to make, send, and interpret packets, and
perform name-caching for performance. They also provide function for
ASCII-to-EBCDIC and EBCDIC-to-ASCII conversion.

The DNS has these major components:


v Domain name space and resource records: Specifications for a tree-structured
name space and the data associated with the names
v Name servers: Server programs that hold information about the domain tree
structure and that set information.
v Resolvers: Programs that extract information from name servers in response to
client requests.

For a description of the individual network routines, see Sockets APIs.

The global information used by these routines is kept in the _res structure. For
information on the _res structure, see the API by category information.

For more information on domain names, see Request for Comment (RFCs)
1034 and 1035.

Format of DNS queries and responses for sockets


This information provides a view of the formats for the queries and responses that
are used between Domain Name System (DNS) functions (routines).

Chapter 6. Improving performance of the socket environment 57


All message packets have a single format divided into five sections as shown in
the following figure:

The header section is always present and specifies which of the remaining sections
are present. (Their presence depends on the type of message.) The question section
contains fields that describe a question to a name server. The answer section holds
the answer to the question. The authority section points toward an authoritative
name server. The additional records section holds information that relates to the
query, but is not strictly an answer for the question.

There are two common methods that enable faster query and response times.
Name compression reduces the size of the messages, while caching data lessens the
amount of network traffic.

Name compression:

To reduce the size of messages, the DNS uses a compression scheme that
eliminates the repetition of domain names in a message. This scheme replaces an
entire domain name with a pointer to a prior occurrence of the same name. A

58 Socket programming
pointer eliminates repetition in the labels list. The following figure shows the
pointer format.

The first two bits are always on. The OFFSET field specifies an offset from the start
of the message. A zero offset would specify the first byte of the ID field in the
header.

This compression scheme allows one of the following forms to represent domain
names in a message:
v A sequence of labels that end in a NULL
v A pointer
v A sequence of labels that end in a pointer

For example, a message may use the following three domain names:
RCHLAND.IBM.COM
IBM.COM
RCHNAME.RCHLAND.IBM.COM

Chapter 6. Improving performance of the socket environment 59


The following figure is an example of how compression might represent these
names with all other fields ignored:

60 Socket programming
The domain name for RCHLAND.IBM.COM is shown at offset 20. The domain
name for RCHNAME.RCHLAND.IBM.COM is shown at offset 46 followed by a
pointer to indicate the location of the full domain name. The domain name for
IBM.COM is defined at offset 64 as a pointer since it was used previously in its
entirety.

Caching data:

You can cache responses to DNS queries by using OS/400 sockets in an effort to
lessen the amount of network traffic. The cache is updated as needed.

If you set RES_AAONLY (authoritative answers only) in _res.options, the query is


always sent on the network. In this case, the system never checks the cache for the
answer. If you do not set RES_AAONLY, the cache is checked for an answer to the
query before any attempt to send it on the network is performed. If the system
finds the answer and the time to live has not expired, the system returns the
answer to the user as the answer to the query. If the time to live has expired, the
entry is removed, and the system sends the query to the network. Also, if the
system does not find the answer in the cache, it sends the query to the network.

The system caches answers from the network if the responses are authoritative.
The system does not cache nonauthoritative answers. Also, the system does not
cache responses that are received as a result of an inverse query.

Chapter 6. Improving performance of the socket environment 61


Format of the DNS header section for sockets
The header section of the Domain Name System (DNS) is always present in the
packet. The following figure shows the fields in the header format.

ID A 16-bit field containing the identifier assigned by the program that


generates the query. The system copies the ID to the corresponding reply
and can use it to match up replies to queries.
QR A 1-bit field specifying whether this message is a query (0) or a response
(1).
OPCODE
A 4-bit field specifying the kind of query in the message. It is set by the
originator of the query and copied to the response. The possible values
defined in the <arpa/nameser.h> header file are as follows:
0 A standard query (QUERY)
1 An inverse query (IQUERY)
2-15 Reserved for future use
AA A 1-bit field containing the authoritative answer. This bit is set if the
responding name server is an authority for the domain name in the
question section. The contents of the answer section may have multiple

62 Socket programming
owner names because of aliases. The AA bit corresponds to the name that
matches the query name or the first owner name in the answer section.
TC A 1-bit field specifying whether this message was truncated due to a
length greater than that permitted on the transmission channel.
RD A 1-bit field that specifies whether or not you want recursion. If set, it
directs the name server to pursue the query recursively.
RA A 1-bit field that is set or cleared in a response denoting whether recursive
query support is available in the name server.
Z A 3-bit field reserved for future use. You must set this field to zero.
RCODE
A 4-bit field to specify the response code. Possible values that are defined
in the <arpa/nameser.h> header file are as follows:
0 NOERROR - No error condition.
1 FORMERR - The name server was unable to interpret the query.
2 SERVFAIL - The name server was unable to process this query due
to a problem with the name server.
3 NXDOMAIN - This code signifies that the domain name in the
query does not exist.
4 NOTIMP - The name server does not support the requested kind of
query.
5 REFUSED - The name server refuses to perform the specified
operation for policy reasons.
6-15 Reserved for future use.
QDCOUNT
An unsigned 16-bit field specifying the number of entries in the question
section. If it is zero, there is no question section.
ANCOUNT
An unsigned 16-bit field specifying the number of resource records in the
answer section. If it is zero, there is no answer section.
NSCOUNT
An unsigned 16-bit field specifying the number of name server resource
records in the authority section. If it is zero, there is no authority section.
ARCOUNT
An unsigned 16-bit field specifying the number of resource records in the
additional records section. If it is zero, there is no additional records
section.

Format of the DNS question section for sockets


The following figure shows the format of the question section for the Domain
Name System (DNS). Usually, this section contains one entry for the QDCOUNT

Chapter 6. Improving performance of the socket environment 63


field in the format shown.

QNAME
A variable-length field specifying a domain name represented in a
sequence of labels. These labels consist of a length followed by that
number of bytes. It is ended by hexadecimal zeros. No padding is used in
this field.
QTYPE
A 16-bit field that specifies the type of query. Possible values are as
follows:
T_A Host address.
T_NS Authoritative server.
T_CNAME
Canonical name.
T_SOA
Start of authority zone.
T_WKS
Well-known service.
T_PTR
Domain name pointer.
T_HINFO
Host information.
T_MX Mail routing information.
T_TXT
Text strings.
T_ANY
Wildcard match.
QCLASS
A 16-bit field specifying the class of the query. Possible values are as
follows:
C_IN ARPA Internet.

64 Socket programming
C_ANY
Wildcard match.

Format of the DNS answer, authority, and additional sections


for sockets
The answer, authority, and additional sections all have the same format for the
Domain Name System (DNS). This format has a variable number of resource
records. The number of records is specified in the corresponding count fields of the
header. The following figure shows the format for each of those records.

NAME
A variable-length field specifying the domain name to which this resource
record pertains.
TYPE A 16-bit field that specifies the type of resource record. These are defined
in the <arpa/nameser.h> header file. This field will determine the format of
RDATA.

Chapter 6. Improving performance of the socket environment 65


CLASS
A 16-bit field specifying the class of data in RDATA. These are defined in
the <arpa/nameser.h> header file.
TTL A 32-bit field specifying the number of seconds this resource record may
cache before discarding it.
RDLENGTH
A 16-bit field specifying the length of RDATA.
RDATA
A variable-length string that describes the resource record. The TYPE and
CLASS fields determine the format.

When to use IP-to-LU mapping


Algorithmic mapping is an alternative to using one-to-one mappings. Do not use
algorithmic IP-to-LU mapping if you want to maintain existing logical unit (LU)
names that you have on a network.

Consider the algorithmic version of IP-to-LU mapping only if you are willing to
rename or add LU names. With algorithmic mapping, you essentially allow the
system to do the mapping according to a predictable pattern. To preserve existing
LU names that do not follow a predictable pattern, use one-to-one mapping.

Use algorithmic IP-to-LU mapping only if both the following are true.
1. The LU names you plan to use for IP over SNA follow a pattern that includes
an unchanged part and a variable part. For example, if your LU names were
NAME01, NAME02, and NAME03, you could use the algorithmic version. If
your LU names do not follow a pattern, (for instance, NEWYORK, PARIS, and
LONDON), use the one-to-one mapping version.
2. Your IP addresses for SNA (the IP subnetwork used for SNA LU names) follow
a pattern. For example, if you are going to use IP addresses 193.1.1.1, 193.1.1.2,
and 193.1.1.3 for IP over SNA, you could use the algorithmic version.

Algorithmic mapping

Depending on the class of your IP address, you have a different minimum number
of system-generated (wildcard) characters in the LU name. The host identifier
portion of the IP address varies by class. (The class A address has more bits of the
IP address assigned to the host identifier portion than the class B address). The
question mark character in the AnyNet/400 mapping commands (location template
(LOCTPL) parameter) represents these system-generated characters. These
characters represent the positions in the LU name that will vary depending on the
IP address that is used in this algorithm.

Internet addresses have two parts:


v A network identifier
v A host identifier on the network

The 4-byte address is divided between the network identifier and the host
identifier in three ways according to the class and size of the network. The
following table shows how the address is divided for class A, B, and C networks.

66 Socket programming
Class and size of Range Network ID Host ID
network
Class A (Large) 1 - 127 Byte 1 Bytes 2, 3, 4
Class B (Medium) 128 - 191 Bytes 1, 2 Bytes 3, 4
Class C (Small) 192 - 223 Bytes 1, 2, 3 Byte 4

For example, 125.1.2.3 identifies a unique Internet address within a class A


network. The network identifier is 125. The host identifier is 1.2.3.

The location-name template is a 3-to-8-character template used by the system for


generating location (LU) names that are based on the IP address specified on
socket system calls. The template must specify some of the characters for the
location name. The system generates the remaining characters based on the class of
the IP address.

Class and size of Range User-specified Minimum number of


network location-name system-generated
characters location-name
characters
Class A (Large) 1 - 127 1 to 3 5
Class B (Medium) 128 - 191 1 to 4 4
Class C (Small) 192 - 223 1 to 6 2

A question mark character (?) identifies system-generated location-name characters.


Each question mark character represents a single character that is generated by the
system. You can use a question mark anywhere within the location-name template
except in the first character position. For example, for a Class B network, the
following location-name templates are valid:
v ABCD????
v A???????
v AB??CD??
v A?B?C?D?

Use the Add IP over SNA Location (ADDIPSLOC) command to configure IP-to-LU
mapping. An example follows:
ADDIPSLOC RMTDEST('192.1.2.0') +
SUBNETMASK('255.255.255.0') +
LOCTPL('NAME??')

The example shows two system-generated characters in the location template


(LOCTPL) parameter; therefore, this is a class C IP address.

When you have entered at least one ADDIPSLOC command, use the Convert IP
Address into Network Identifier/Location Name (CVTIPSIFC) command and the
Convert Network Identifier/Location Name into IP Address (CVTIPSLOC)
command to see if you can map the IP addresses to LU names or vice versa. As a
general practice, you can use these commands to determine how the mapping is
done.

If your mapping is not successful, do either or both of the following:

Chapter 6. Improving performance of the socket environment 67


v Remove your mapping entry (by using the Remove IP over SNA Location
(RMVIPSLOC) command) and add a new entry (by using the Add IP over SNA
Location (ADDIPSLOC) command).
v Use some one-to-one mapping entries in addition to or instead of algorithmic
entries. Do this if the system-generated part of your LU names contains
characters not returned by the algorithm (the letters E, I, O, and U). Another
case where the algorithm may not work is when the host portion of the IP
address is not large enough to accommodate the characters in your LU name.

68 Socket programming
Chapter 7. Examples: Using sockets
These examples represent the most commonly used socket applications while
managing to cover a variety of socket environments and connection configurations.
v Telephony domain socket
These examples use the telephony domain socket to make a connection and to
accept a call.
v Socket configuration
The socket configuration examples show how to configure the following Internet
Protocol (IP) and Systems Network Architecture (SNA) environments:
– A single IP network and single SNA network
– A multiple IP networks and multiple SNA networks
– A change to an existing configuration
v Socket API and SSL
These examples contain C programs that use the AS/400 sockets APIs. The
examples in the following table show how to use the sockets APIs in a number
of different environments. The Information Center provides more information
concerning general API descriptions and specific socket APIs.

Environment Examples provided Additonal information


Transmission Control These server program and Connection-oriented
Protocol (TCP) client program examples environment information
illustrate socket APIs written
for a connection-oriented
protocol:
– TCP server and client
– SIOCESNDQ server and
client
User Datagram Protocol The connectionless protocol Connectionless environment
(UDP) server and client examples information
illustrate the sockets APIs
that are written for the UDP
client and server.
Connection-oriented server You can design a Connection-oriented server
designs connection-oriented socket designs information
server on the AS/400 in a
number of ways. The most
common examples are as
follows:
– Iterative server example
– spawn() server and
spawn() worker example
– givedescriptor() server
and takedescriptor()
worker example
– sendmsg() server and
recvmsg() worker example
– Multiple accept() server
and multiple accept()
worker example

© Copyright IBM Corp. 2000 69


Environment Examples provided Additonal information
Connection-oriented common This example provides the This client job works with all
client code for the client job. five types of servers that are
listed in the
connection-oriented server
designs examples
Threadsafe network routines This example code uses the
gethosbyaddr_r() API.
IP multicasting These examples show how to
send and receive multicast
datagrams.
AS/400 SSL The AS/400 Secure Sockets
Layer (SSL) APIs provide
secure communications
between a server and a
client.
AS/400 send_file() and The AS/400 environment
accept_and_recv() uses the send_file() and
accept_and_recv() APIs to
transfer the contents of a file
between processes on a
network.

v ILE RPG to ILE C sockets


These examples are found in chapter 5 of the Who Knew You Could Do That

with RPG IV? A SorcererÆs Guide to System Access and More redbook.

Example: Making a connection using the telephony domain socket


Programs can communicate with each other through a telephony domain socket.
Use the following code to enable the socket to make the connection with a client.
#include <stdio.h> /* String Functions */
#include <string.h> /* String Functions */
#include <miptrnam.h> /* Pointer types */

#include <sys/socket.h> /* Sockets */


#include <nettel/tel.h> /* Telephony address family */
#include <errno.h> /* Error codes */
#include <sys/ioctl.h> /* Error codes */

int main() {
/******************************************************************/
/* Miscellaneous declares */
/******************************************************************/
int xSock, xRC, xLength;

/******************************************************************/
/* Resolve device name to system pointer */
/******************************************************************/
_SYSPTR pDev; /* System pointer to device */
_RSLV_Template_T xTemp; /* Template for resolve */
char pName[]="FRED "; /* Device name */
struct TelResource xResource; /* SIOCSTELRSC structure */

/******************************************************************/
/* Socket address structure */
/******************************************************************/
struct sockaddr_tel xAddr;

70 Socket programming
/******************************************************************/
/* Buffers */
/******************************************************************/
char pSendBuffer[1024];
char pRecvBuffer[1024];

/******************************************************************/
/* Open a socket */
/******************************************************************/
xSock = socket(AF_TELEPHONY,SOCK_STREAM,0);
if (xSock<0) {
perror("socket() failed");
return(-1);
}

/******************************************************************/
/* Associate the socket with a device */
/* ...resolve the device name to a system pointer */
/* ...fill in the structure for this request */
/* ...issue the ioctl to perform the association */
/******************************************************************/
memset(&xTemp,0x00,sizeof(xTemp));
memcpy(xTemp.Obj.Name, pName, 30);
xTemp.Obj.Type_Subtype = WLI_DEVD;
xTemp.Auth = _AUTH_NONE;
_RSLVSP2(&pDev,&xTemp);

memset(&xResource,0x00,sizeof(xResource));
xResource.trCount=1;
xResource.trResourceList=&pDev

xRC=ioctl(xSock,SIOCSTELRSC,&xResource);
if (xRC<0) {
perror("ioctl() failed");
close(xSock);
return(-1);
}

/******************************************************************/
/* Connect to a remote resource (dial a call) */
/******************************************************************/
memset(&xAddr,0x00,sizeof(xAddr));
xAddr.stel_family=AF_TELEPHONY;
xAddr.stel_addr.t_len=11;
memcpy(xAddr.stel_addr.t_addr,"18005551212",11);
xRC=connect(xSock,(struct sockaddr*)&xAddr,sizeof(xAddr));
if (xRC<0) {
perror("connect() failed");
close(xSock);
return(-1);
}

/******************************************************************/
/* Send the contents of the send buffer */
/******************************************************************/
xRC=send(xSock,pSendBuffer,1024,0);
if (xRC<0) {
perror("send() failed");
close(xSock);
return(-1);
}

/******************************************************************/
/* Receive a reply */
/******************************************************************/

Chapter 7. Examples: Using sockets 71


xRC=recv(xSock,pRecvBuffer,1024,0);
if (xRC<0) {
perror("recv() failed");
close(xSock);
return(-1);
}

/******************************************************************/
/* All done, close and return */
/******************************************************************/
close(xSock);
return(0);
}

Example: Accepting a connection using the telephony domain socket


Programs can communicate with each other through a telephony domain socket.
Use the following code to enable the socket to accept a connection from the server.
#include <stdio.h> /* String Functions */
#include <string.h> /* String Functions */
#include <miptrnam.h> /* Pointer types */

#include <sys/socket.h> /* Sockets */


#include <nettel/tel.h> /* Telephony address family */
#include <errno.h> /* Error codes */
#include <sys/ioctl.h> /* Error codes */

int main() {

/*********************************************************************/
/* Micellaneous declares */
/*********************************************************************/
int xSock,xNewSock,xRC,xLength;

/*****************************************************************/
/* Resolve device name to system pointer data areas */
/*****************************************************************/
_SYSPTR pDev; /* System pointer to device */
_RSLV_Template_T xTemp; /* Template for resolve */
char pName[]="GEORGE "; /* Device name */
struct TelResource xResource; /* SIOCSTELRSC structure */

/*****************************************************************/
/* Socket address structure */
/*****************************************************************/
struct sockaddr_tel xAddr;

/*****************************************************************/
/* Buffers */
/*****************************************************************/
char pSendBuffer[1024];
char pRecvBuffer[1024];

/******************************************************************/
/* Open a socket */
/******************************************************************/
xSock = socket(AF_TELEPHONY,SOCK_STREAM,0);
if (xSock<0) {
perror("socket() failed");
return(-1);
}

/*****************************************************************/
/* ...first, resolve the device name to a system pointer */
/* ...next, fill in the structure for this request */
/* ...finally, issue the ioctl to perform the association */

72 Socket programming
/*****************************************************************/
memset(&xTemp,0x00,sizeof(xTemp));
memcpy(xTemp.Obj.Name, pName, 30);
xTemp.Obj.Type_Subtype = WLI_DEVD;
xTemp.Auth = _AUTH_NONE;
_RSLVSP2(&pDev,&xTemp);

memset(&xResource,0x00,sizeof(xResource));
xResource.trCount=1;
xResource.trResourceList=&pDev

xRC=ioctl(xSock,SIOCSTELRSC,&xResource);
if (xRC<0) {
perror("ioctl() failed");
close(xSock);
return(-1);
}

/*****************************************************************/
/* Bind to a local number (using TELADDR_ANY means to accept */
/* calls for any number in the inbound connection list's entries)*/
/*****************************************************************/
memset(&xAddr,0x00,sizeof(xAddr));
xAddr.stel_family=AF_TELEPHONY;
xAddr.stel_addr.t_len=TELADDR_LEN;
memcpy(xAddr.stel_addr.t_addr,TELADDR_ANY,TELADDR_LEN);
xRC=bind(xSock,(struct sockaddr*)&xAddr,sizeof(xAddr));
if (xRC<0) {
perror("bind() failed");
close(xSock);
return(-1);
}

/*****************************************************************/
/* Listen for incoming calls */
/*****************************************************************/
xRC=listen(xSock,5);
if (xRC<0) {
perror("listen() failed");
close(xSock);
return(-1);
}
/*****************************************************************/
/* Accept an incoming call */
/*****************************************************************/
memset(&xAddr,0x00,sizeof(xAddr));
xLength = sizeof(xAddr);
xNewSock=accept(xSock,(struct sockaddr*)&xAddr,&xLength);
if (xNewSock<0) {
perror("accept() failed");
close(xSock);
return(-1);
}

/*****************************************************************/
/* Receive some data */
/*****************************************************************/
xRC=recv(xNewSock,pRecvBuffer,1024,0);
if (xRC<0) {
perror("recv() failed");
close(xSock);
close(xNewSock);
return(-1);
}

/*****************************************************************/
/* Send a reply */

Chapter 7. Examples: Using sockets 73


/*****************************************************************/
xRC=send(xNewSock,pSendBuffer,1024,0);
if (xRC<0) {
perror("send() failed");
close(xSock);
close(xNewSock);
return(-1);
}

/*****************************************************************/
/* All done, close both sockets and return */
/*****************************************************************/
close(xSock);
close(xNewSock);
return(0);
}

Example: Connecting a single IP network with a single SNA network


This example lists the commands that you use to configure a single Internet
Protocol (IP) network with a single Systems Network Architecture (SNA) network.
You can perform these same functions by using a menu interface initiated by
options from the Configure IP over SNA (CFGIPS) menu.

The choice of IP addresses that are shown in the example leaves gaps in the
numbering of the IP addresses. The gaps allow room for future additions to the
network. For example, new departments in region 1 would use IP addresses
between 123.4.5.2 and 123.4.5.100, while other regions would use IP addresses
between 123.4.5.101 and 123.4.5.200.

Allow room for future additions of IP addresses and follow a pattern that makes
sense for your network. By following a pattern, you can avoid the confusion that is
caused when two systems try to use the same IP addresses.

The example provided uses a one-to-one mapping of IP addresses to LU names.


The example uses a subnet mask of *HOME (this is the same as *HOST). This
mapping means that exactly one IP address makes up each subnetwork group.

You can accomplish the IP-to-LU name-mapping in two different ways. One way is
a one-to-one mapping by specifying a corresponding LU name for each IP address.
The other way to is use an algorithm to map portions of the IP address to portions
of the LU name. See “When to use IP-to-LU mapping” on page 66 for more
information.

You would use this one-to-one mapping option for the following reasons:
v You do not want to define new LU names.
v The existing LU names do not follow a pattern that would allow the
algorithmic-mapping option to map the IP address to LU names.

This command example is based on the assumption that an existing IP network


has a network ID of 123.4.5.0. The following figure shows an existing SNA network

74 Socket programming
that the system is to add to the IP network.

The command example uses the subnet mask of 255.255.255.0. Performing an AND
operation on 123.4.5.0 and 255.255.255.0 gives a subset of IP network addresses of
123.4.5.1 through 123.4.5.254. These are the addresses for the IP to use over SNA
support. This allows for 253 different IP addresses and up to 253 different SNA LU
names.

Chapter 7. Examples: Using sockets 75


The following figure shows the IP addresses that are assigned to the SNA network.

The example shows you how to use of the Add IP over SNA Location
(ADDIPSLOC) command to specify these pieces of information:
v The IP address that you have assigned to this system. Each system requires a
unique IP address.
v The subnet mask.

For each system in the network, use the ADDIPSLOC command to map the IP
address of that system to its SNA network ID and SNA local LU name. All the
systems use the ADDIPSLOC command to allow any system in the network to
communicate with any other system in the network. Each system must be able to
map both its IP address and the destination IP address to the corresponding LU
names. If you want all systems to be able to communicate with each other, all
systems must map all IP-over-SNA addresses to the correct LU names.

On department 1’s system, use the following commands:


ADDIPSIFC INTNETADR('123.4.5.1')
SUBNETMASK('255.255.255.0')
ADDIPSLOC RMTDEST('123.4.5.1') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(BLUE1)
ADDIPSLOC RMTDEST('123.4.5.2') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(SILVER1)
ADDIPSLOC RMTDEST('123.4.5.100') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(RED1)

76 Socket programming
ADDIPSLOC RMTDEST('123.4.5.200') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(PURPLE1)
CHGNETA ALWANYNET(*YES)

On department 2’s system, use the following commands:


ADDIPSIFC INTNETADR('123.4.5.2') +
SUBNETMASK('255.255.255.0')
ADDIPSLOC RMTDEST('123.4.5.1') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(BLUE1)
ADDIPSLOC RMTDEST('123.4.5.2') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(SILVER1)
ADDIPSLOC RMTDEST('123.4.5.100') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(RED1)
ADDIPSLOC RMTDEST('123.4.5.200') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(PURPLE1)
CHGNETA ALWANYNET(*YES)

On region 1’s system, use the following commands:


ADDIPSIFC INTNETADR('123.4.5.100')
SUBNETMASK('255.255.255.0')
ADDIPSLOC RMTDEST('123.4.5.1') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(BLUE1)
ADDIPSLOC RMTDEST('123.4.5.2') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(SILVER1)
ADDIPSLOC RMTDEST('123.4.5.100') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(RED1)
ADDIPSLOC RMTDEST('123.4.5.200') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(PURPLE1)
CHGNETA ALWANYNET(*YES)

On the headquarters’ system, use the following commands:


ADDIPSIFC INTNETADR('123.4.5.200')
SUBNETMASK('255.255.255.0')
ADDIPSLOC RMTDEST('123.4.5.1') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(BLUE1)
ADDIPSLOC RMTDEST('123.4.5.2') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(SILVER1)
ADDIPSLOC RMTDEST('123.4.5.100') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(RED1)
ADDIPSLOC RMTDEST('123.4.5.200') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(PURPLE1)
CHGNETA ALWANYNET(*YES)
Notes:
1. On each system that is configured, the commands listed include a Change
Network Attributes (CHGNETA) command to activate the AnyNet support on
the AS/400 system.
2. Except for the initial Add IP over SNA Interface (ADDIPSIFC) command, each
set of commands is basically the same. They need to be the same because this
example assumes that all nodes have a need to talk to each other.
You can take advantage of these similar commands by shipping a control
language (CL) program to each system.

Example: Connecting multiple IP networks with multiple SNA networks


This example lists the commands that you use to configure multiple Internet
Protocol (IP) networks with multiple Systems Network Architecture (SNA)
networks. You can perform these same functions by using a menu interface
initiated by options from the Configure IP over SNA (CFGIPS) menu.

Chapter 7. Examples: Using sockets 77


This is a more complicated scenario than connecting a single IP network with a
single SNA network. Each company that wants to communicate with IP over SNA
has a different IP network ID and a different SNA network ID. Also, each company
wants to communicate with its network, as well as with the other company’s
network.

This command example is based on the following assumptions:


v Company 1 has an existing IP network with a network ID of 222.3.4.0.
v Company 2 has an existing IP network with a network ID of 111.2.0.0.
v Only the NEWYORK system of Company 1 and the NYC system of Company 2
need to contact the other company’s network node (or system).

78 Socket programming
The following figure shows the existing SNA network.

To determine IP addresses for the SNA network, Company 1 uses a subnet mask of
255.255.255.128. This allows SNA to use IP addresses of 222.3.4.1 through
222.3.4.127. This works out to 126 different IP addresses and up to 126 different

Chapter 7. Examples: Using sockets 79


SNA LU names. Company 2 will use a subnet mask of 255.255.255.0. This allows
you to use the IP addresses of 111.2.3.1 through 111.2.3.254. This provides 253
different IP addresses and up to 253 different SNA LU names.

The following figure shows the IP addresses that are given out to the SNA
network.

On Company 1’s San Francisco system, use the following commands:

80 Socket programming
ADDIPSIFC INTNETADR('222.3.4.16')
SUBNETMASK('255.255.255.128')
ADDIPSLOC RMTDEST('222.3.4.16') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(SANFRAN)
ADDIPSLOC RMTDEST('222.3.4.7') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(NEWYORK)
ADDIPSLOC RMTDEST('222.3.4.76') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(HOUSTON)
CHGNETA ALWANYNET(*YES)

On Company 1’s New York system, use the following commands:


ADDIPSIFC INTNETADR('222.3.4.7')
SUBNETMASK('255.255.255.128')
ADDIPSLOC RMTDEST('222.3.4.16') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(SANFRAN)
ADDIPSLOC RMTDEST('222.3.4.7') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(NEWYORK)
ADDIPSLOC RMTDEST('222.3.4.76') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(HOUSTON)
ADDIPSLOC RMTDEST('111.2.3.200') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(NYC)
ADDIPSRTE RMTDEST('111.2.3.200') SUBNETMASK(*HOST)
NEXTHOP('222.3.4.7')
CHGNETA ALWANYNET(*YES)

The commands used for the New York system need to include a route definition.
The New York system of Company 1 is connected as an intercompany link to the
New York system of Company 2. Specify the route definition with the Add IP over
SNA Route Entry (ADDIPSRTE) command.

On Company 1’s Houston system, use the following commands:


ADDIPSIFC INTNETADR('222.3.4.76')
SUBNETMASK('255.255.255.128')
ADDIPSLOC RMTDEST('222.3.4.16') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(SANFRAN)
ADDIPSLOC RMTDEST('222.3.4.7') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(NEWYORK)
ADDIPSLOC RMTDEST('222.3.4.76') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(HOUSTON)
CHGNETA ALWANYNET(*YES)

On Company 2’s Hong Kong system, use the following commands:


ADDIPSIFC INTNETADR('111.2.3.100')
SUBNETMASK('255.255.255.0')
ADDIPSLOC RMTDEST('111.2.3.100') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(HONGKONG)
ADDIPSLOC RMTDEST('111.2.3.1') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(LONDON)
ADDIPSLOC RMTDEST('111.2.3.200') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(NYC)
ADDIPSLOC RMTDEST('111.2.3.246') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(PARIS)
CHGNETA ALWANYNET(*YES)

On Company 2’s London system, use the following commands:


ADDIPSIFC INTNETADR('111.2.3.1')
SUBNETMASK('255.255.255.0')
ADDIPSLOC RMTDEST('111.2.3.100') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(HONGKONG)
ADDIPSLOC RMTDEST('111.2.3.1') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(LONDON)
ADDIPSLOC RMTDEST('111.2.3.200') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(NYC)

Chapter 7. Examples: Using sockets 81


ADDIPSLOC RMTDEST('111.2.3.246') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(PARIS)
CHGNETA ALWANYNET(*YES)

On Company 2’s New York system, use the following commands:


ADDIPSIFC INTNETADR('111.2.3.200')
SUBNETMASK('255.255.255.0')
ADDIPSLOC RMTDEST('111.2.3.100') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(HONGKONG)
ADDIPSLOC RMTDEST('111.2.3.1') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(LONDON)
ADDIPSLOC RMTDEST('111.2.3.200') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(NYC)
ADDIPSLOC RMTDEST('111.2.3.246') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(PARIS)
ADDIPSLOC RMTDEST('222.3.4.7') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(NEWYORK)
ADDIPSRTE RMTDEST('222.3.4.7') SUBNETMASK(*HOST)
NEXTHOP('111.2.3.200')
CHGNETA ALWANYNET(*YES)

Just as with Company 1, the New York system of Company 2 needs to include a
route definition. Specify the route definition with the Add IP over SNA Route
Entry (ADDIPSRTE) command.

On Company 2’s Paris system, use the following commands:


ADDIPSIFC INTNETADR('111.2.3.246')
SUBNETMASK('255.255.255.0')
ADDIPSLOC RMTDEST('111.2.3.100') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(HONGKONG)
ADDIPSLOC RMTDEST('111.2.3.1') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(LONDON)
ADDIPSLOC RMTDEST('111.2.3.200') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(NYC)
ADDIPSLOC RMTDEST('111.2.3.246') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(PARIS)
CHGNETA ALWANYNET(*YES)

Note: This example shows connections for passing data between Company 1 and
Company 2. Consider data security when passing data between separate
companies. Session-level security is available within an SNA network. When
you use both an SNA network and an IP network, the application itself
needs to provide security for the data.

Example: Changing a configuration


This example lists the commands that you use to change configuration functions.
You can perform these same functions by using a menu interface initiated by
options from the Configure IP over SNA (CFGIPS) menu.

Read the “Example: Connecting a single IP network with a single SNA network”
on page 74 or “Example: Connecting multiple IP networks with multiple SNA
networks” on page 77 examples before reading this one. The coding information
that changes in this example originates from the coding information in those
examples.

Assume that the change you want to make is to allow the PARIS system in
Company 2 to connect to the HOUSTON system in Company 2. The first thing to
do is have the PARIS system start a connection to the HOUSTON system. To do
this, use the following commands:

82 Socket programming
v Use the following command to tell the PARIS system what SNA NETID and LU
name to use:
ADDIPSLOC RMTDEST('222.3.4.76') SUBNETMASK(*HOST)
RMTNETID(COMPANY1) LOCTPL(HOUSTON)
v Tell the PARIS system that the remote destination is in the SNA network but not
in the IP subnetworks that are defined by the IP-over-SNA interfaces. To do this,
use the following command:
ADDIPSRTE RMTDEST('222.3.4.7') SUBNETMASK(*HOST)
NEXTHOP('111.2.3.246')

Have the HOUSTON system start a connection to the PARIS system. To do this,
use the following commands:
ADDIPSLOC RMTDEST('111.2.3.246') SUBNETMASK(*HOST)
RMTNETID(COMPANY2) LOCTPL(PARIS)
ADDIPSRTE RMTDEST('111.2.3.246') SUBNETMASK(*HOST)
NEXTHOP('222.3.4.7')

Examples: TCP connection-oriented client/server introduction


These server and client examples illustrate socket APIs written for a
connection-oriented protocol such as Transmission Control Protocol (TCP),
including those used by SIOCSENDQ.

The TCP server example and SIOCESNDQ server example use the following
sequence of function calls:
v socket()
v bind()
v listen()
v accept()

The TCP client example and SIOCESNDQ client example use the following
sequence of function calls:
v socket()
v connect()

Chapter 7. Examples: Using sockets 83


The following figure illustrates the client/server relationship of the sockets API for
a connection-oriented protocol.

Examples: Connecting a TCP server and client


Following are connection-oriented examples where sockets use TCP to connect a
server to a client, and a client to a server.

Example: Connecting a TCP server to a client


/***********************************************/
/* Header files needed to use the sockets API. */
/* File contain Macro, Data Type and Structure */
/* definitions along with Function prototypes. */
/***********************************************/
/* in qcle/h file */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* in qsysinc library */
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>

84 Socket programming
/* BufferLength is 250 bytes */
#define BufferLength 250

/* Server port number */


#define SERVPORT 3005

void main()
{
/*********************************************/
/* Variable and structure definitions. */
/*********************************************/
int sd, sd2, rc, length = sizeof(int);
int totalcnt = 0, on = 1;
char temp;
char buffer[BufferLength];
struct sockaddr_in serveraddr;
fd_set read_fd;

struct timeval timeout;

timeout.tv_sec = 10;
timeout.tv_usec = 0;

/**********************************************/
/* The socket() function returns a socket descriptor */
/* representing an endpoint. The statement also */
/* identifies that the INET (Internet Protocol) */
/* address family with the TCP transport (SOCK_STREAM) */
/* will be used for this socket. */
/************************************************/
/* Get a socket descriptor */
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket() failed");
exit(-1);
}

/**********************************************/
/* The setsockopt() function is used to allow */
/* the local address to be reused when the server */
/* is restarted before the required wait time */
/* expires. */
/***********************************************/
/* Allow socket descriptor to be reuseable */
if ((rc = setsockopt(sd, SOL_SOCKET,
SO_REUSEADDR,
(char *)&on,
sizeof(on))) < 0)
{
perror("setsockopt() failed");
close(sd);
exit(-1);
}

/* bind to an address */
memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVPORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

/************************************************/
/* After the socket descriptor is created, a bind() */
/* function gets a unique name for the socket. */
/* socket. In this example, the user sets the */
/* s_addr to zero, which allows the system to */
/* connect to any client that used port 3005. */

Chapter 7. Examples: Using sockets 85


/*************************************************/
if ((rc = bind(sd
(struct sockaddr *)&serveraddr,
sizeof(serveraddr))) < 0)
{
perror("bind() failed");
close(sd);
exit(-1);
}

/*************************************************/
/*The listen() function allows the server to accept */
/* incoming client connections. In this example, */
/* the backlog is set to 10. This means that the */
/* system can queue 10 connection requests before */
/* the system starts rejecting incoming requests.*/
/*************************************************/
/* Up to 10 clients can be queued */
if ((rc = listen(sd, 10)) < 0)
{
perror("listen() failed");
close(sd);
exit(-1);
}

printf("Ready for client connect().\n");


/***********************************************/
/* The server will accept a connection request */
/* with this accept() function, provided the */
/* connection request does the following: */
/* - Is part of the same address family */
/* - Uses streams sockets (TCP) */
/* - Attempts to connect to the specified port */
/***********************************************/
/* accept() the incoming connection request. */
if ((sd2 = accept(sd, (struct sockaddr *)NULL,
NULL)) < 0)
{
perror("accept() failed");
close(sd);
exit(-1);
}

/***********************************************/
/* The select() function allows the process to */
/* wait for an event to occur and to wake up */
/* the process when the event occurs. In this */
/* example, the system notifies the process */
/* only when data is available to read. */
/***********************************************/
/* Wait for up to 10 seconds on */
/* select() for data to be read. */
FD_ZERO(&read_fd);
FD_SET(sd2,&read_fd);
rc = select(sd2+1,&read_fd,NULL,NULL,&timeout);
if ( (rc == 1) && (FD_ISSET(sd2,&read_fd)) )
{
/* Read data from the client. */
totalcnt = 0;
while(totalcnt < BufferLength)
{
/***********************************************/
/* When select() indicates that there is data */
/* available, use the read() function to read */
/* 250 bytes of the character a's that the */
/* client sent. */
/***********************************************/

86 Socket programming
/* read() from client */
rc = read(sd2,
&buffer[totalcnt],
BufferLength-totalcnt);
if (rc < 0)
{
perror("read() failed");
close(sd);
close(sd2);
exit(-1);
}
else if (rc == 0)
{
printf("partner program has issued a close()");
close(sd);
close(sd2);
exit(-1);
}
else
totalcnt += rc;
}
}
else if (rc < 0)
{
perror("select() failed");
close(sd);
close(sd2);
exit(-1);
}
else /* rc == 0 */
{
printf("select() timed out.\n");
close(sd);
close(sd2);
exit(-1);
}

/************************************/
/* Echo the 250 bytes of a's back */
/* to the client by using the write() */
/* function. */
/************************************/
/* write() the 250 bytes of 'a's */
/* back to the client. */
rc = write(sd2, buffer, totalcnt);
if (rc != totalcnt)
{
perror("write() failed");

/* Get the error number. */


rc = getsockopt(sd2, SOL_SOCKET, SO_ERROR,
&temp, &length);
if (rc == 0)
{
/********************************/
/* Print out the asynchronously */
/* received error. */
/********************************/
errno = temp;
perror("SO_ERROR was");
}
close(sd);
close(sd2);
exit(-1);
}

/*****************************************/

Chapter 7. Examples: Using sockets 87


/* When the data has been sent, close() */
/* the socket descriptor that was returned */
/* from the accept() verb and close() the */
/* original socket descriptor. */
/*****************************************/
/* Close the connection to the client and */
/* close the server listening socket. */
/******************************************/
close(sd2);
close(sd);
exit(0);
}

Example: Connecting a TCP client to a server

The following example shows how to connect a socket client program to a


connection-oriented server.
/***********************************************/
/* Header files needed to use the sockets API. */
/* File contains Macro, Data Type and */
/* Structure definitions along with Function */
/* prototypes. */
/***********************************************/
/* in qcle/h file */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* in qsysinc library */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>

/* BufferLength is 250 bytes */


#define BufferLength 250

/* Host name of server system */


#define SERVER "SERVERHOSTNAME"

/* Server's port number */


#define SERVPORT 3005

/* Pass in 1 parameter which is either the */


/* address or host name of the server, or */
/* set the server name in the #define */
/* SERVER. */
void main(int argc, char *argv[])
{
/****************************************/
/* Variable and structure definitions. */
/****************************************/
int sd, rc, length = sizeof(int);
struct sockaddr_in serveraddr;
char buffer[BufferLength];
char server[255];
char temp;
int totalcnt = 0;
struct hostent *hostp;

/* will send 250 bytes of 'a' */


memset(buffer, 'a', 250);

88 Socket programming
/******************************************/
/* The socket() function returns a socket */
/* descriptor representing an endpoint. */
/* The statement also identifies that the */
/* INET (Internet Protocol) address family */
/* with the TCP transport (SOCK_STREAM) */
/* will be used for this socket. */
/******************************************/
/* get a socket descriptor */
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket() failed");
exit(-1);
}

if (argc > 1)
strcpy(server, argv[1]);
else
strcpy(server, SERVER);

memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));


serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVPORT);
if ((serveraddr.sin_addr.s_addr = inet_addr(server))
== (unsigned long)INADDR_NONE)
{
/*************************************************/
/* When passing the host name of the server as a */
/* parameter to this program, use the gethostbyname() */
/* function to retrieve the address of the host server. */
/***************************************************/
/* get host address */
hostp = gethostbyname(server);
if (hostp == (struct hostent *)NULL)
{
printf("HOST NOT FOUND --> ");
/* h_errno is usually defined */
/* in netdb.h */
printf("h_errno = %d\n",h_errno);
close(sd);
exit(-1);
}
memcpy(&serveraddr.sin_addr,
hostp->h_addr,
sizeof(serveraddr.sin_addr));
}

/***********************************************/
/* After the socket descriptor is received, the */
/* connect() function is used to establish a */
/* connection to the server. */
/***********************************************/
/* Connect() to server. */
if ((rc = connect(sd,
(struct sockaddr *)&serveraddr,
sizeof(serveraddr))) < 0)
{
perror("connect() failed");
close(sd);
exit(-1);
}

/*********************************************/
/* Send 250 bytes of a's to the server using */
/* the write() function. */
/*********************************************/
/* Write() 250 'a's to the server. */

Chapter 7. Examples: Using sockets 89


rc = write(sd, buffer, sizeof(buffer));
if (rc < 0)
{
perror("write() failed");
rc = getsockopt(sd, SOL_SOCKET, SO_ERROR,
&temp, &length);
if (rc == 0)
{
/* Print out the asynchronously */
/* received error. */
errno = temp;
perror("SO_ERROR was");
}
close(sd);
exit(-1);
}

totalcnt = 0;
while(totalcnt < BufferLength)
{
/***************************************/
/* Wait for the server to echo the 250 */
/* bytes of a's back by using the */
/* read() function. */
/***************************************/
/* Read data from the server. */
rc = read(sd, &buffer[totalcnt],
BufferLength-totalcnt);
if (rc < 0)
{
perror("read() failed");
close(sd);
exit(-1);
}
else if (rc == 0)
{
printf("partner program has issued a close()");
close(sd);
exit(-1);
}
else
totalcnt += rc;
}

/****************************************/
/* When the data has been read, close() */
/* the socket descriptor. */
/****************************************/
/* Close socket descriptor from client side. */
close(sd);
exit(0);
}

Examples: Connecting an SIOCSENDQ server and client


Following are socket server program examples that use TCP. In this example, the
SIOCSENDQ server does not perform an application-level acknowledgement for
the receipt of data. This is to simulate a SCIOSENDQ client that uses a server that
it has no control over from a programming standpoint. If it did have control, it
should use an application-level acknowledgement.

Example: Connecting an SIOCSENDQ server to a client


#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>

90 Socket programming
#include <netinet/in.h>

#define SERVER_PORT 12121

main(int argc, char *argv[])


{
int rc;
int listen_sd, accept_sd;
char buffer[0x10000]; /* 64K */
struct sockaddr_in addr;
/*****************************************/
/* Create an AF_INET stream socket to receive */
/* incoming connections on */
/******************************************/
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
/*****************************************/
/* The socket() function returns a socket descriptor */
/* representing an endpoint. The statement also */
/* identifies that the INET (Internet Protocol) */
/* address family with the TCP transport (SOCK_STREAM) */
/* will be used for this socket. */
/*******************************************/
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}

/******************************************/
/* Bind the socket */
/******************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);
rc = bind(listen_sd,
(struct sockaddr *)&addr, sizeof(addr));
if rc < 0
/*******************************************/
/* After the socket descriptor is created, */
/* a bind() function is done to get */
/* a unique address for the socket. */
/*******************************************/
{
perror("bind() failed");
close(listen_sd);
exit(-1);
}

/*******************************************/
/* Set the listen backlog */
/* The listen() function allows the server */
/* to accept incoming client */
/*******************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}

/*********************************************/
/* Inform the user that the server is ready */
/*********************************************/
printf("The server is ready\n");
/*******************************************/

Chapter 7. Examples: Using sockets 91


/* Wait for an incoming connection */
/*******************************************/
printf("Waiting on accept()\n");
/*******************************************/
/* The server accepts a connection request */
/* with this accept() function */
/*******************************************/
accept_sd = accept(listen_sd, NULL, NULL);
if (accept_sd <0)
{
perror("accept() failed");
close(listen_sd);
exit(-1);
}
printf(" accept completed successfully\n");
/********************************************/
/* To assist in the purpose of this example,*/
/* the server sleeps 1 second before it continues.*/
/* This gives the client a chance to issue the */
/* send() function first.
/**********************************************/

sleep (1)
/**********************************************/
/* Receive a message from the client */
/**********************************************/
printf(" wait for client to send us a message\n");
do
/*********************************************/
/* The recv() function will receive the data */
/* sent from the client. This is done in a */
/* loop as all of the data may not have arrived */
/* at the time of the first recv(). */
/*********************************************/
{
rc = recv(accept_sd, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("recv() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}
printf("Received %d bytes\n", rc);
}while (rc !=0);
/**************************************/
/* Close down the incoming connection */
/* and the connected socket descriptor */
/*************************************/
close(accept_sd);
/*************************************/
/* Close down the listen socket and */
/* listen socket descriptor */
/*************************************/
close(listen_sd);
}

Example: Connecting an SIOCENDQ client to a server

The following is a socket client program example that uses TCP and the
SIOCENDQ ioctl(). In this example, the server program does not perform
application-level acknowledgement for the receipt of data.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>

92 Socket programming
#include <sys/ioctl.h>

#define SERVER_PORT 12121

main(int argc, char *argv[])


{
int len, rc;
int sockfd;
int bufflen;
char buffer[0x10000]; /* 64K */
struct sockaddr_in addr;
/****************************************/
/* Initialize buffer length field */
/****************************************/
bufflen = sizeof(buffer);
/****************************************/
/* Create an AF_INET stream socket. The */
/* socket() function returns a socket */
/* descriptor that represents an endpoint.*/
/* The statement also identifies that the */
/* INET (Internet Protocol) address family */
/* with the TCP transport (SOCK_STREAM) */
/* will be used for this socket */
/****************************************/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if sockfd < 0)
{
perror("socket");
exit(-1);
}

/****************************************/
/* Initialize the socket address structure */
/****************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);
/****************************************/
/* Connect to the server. The connect() */
/* function is used to establish a connection */
/* to the server */
/****************************************/
rc = connect(sockfd,
(struct sockaddr *)&addr,
sizeof(struct sockaddr_in));
if (rc < 0)
{
perror("connect");
close(sockfd);
exit(-1);
}
printf("Connect completed.\n");
/****************************************/
/* Send data buffer to the server job. */
/* Send the data to the server using the */
/* send() function. */
/****************************************/
len = send(sockfd, buffer, bufflen, 0);
if (len !=bufflen)
{
perror("send");
close(sockfd);
exit(-1);
}printf("%d bytes sent\n", len);

/****************************************/

Chapter 7. Examples: Using sockets 93


/* verify data send has reached remote driver */
/****************************************/
do
{
/****************************************/
/* After the send, issue an ioctl(SIOCSENDQ) */
/* call to determine if the sent data has */
/* been acknowledged by the remote TCP */
/* driver. This example has the ioctl performed */
/* in a loop until all data has been acknowledged */
/****************************************/
rc=ioctl(sockfd, SIOCSENDQ, &len);
if (rc < 0)
{
perror("ioctl(SIOCSENDQ)");
close(sockfd);
exit(-1);
}

if (len == 0)
printf("All data acknowledged by remote driver.\n");
else
{
printf("%d bytes yet to be acknowledged.\n", len);
/****************************************/
/* The process sleeps for one second each */
/* iteration of the loop that does not have */
/* a len of 0. NOTE: In a real application, */
/* alternate processing could be performed */
/* at this time instead of a sleep. */
/****************************************/
sleep(1);
}
} while (len !=0);
/****************************************/
/* Close down the socket. After the data */
/* has been acknowledged by the remote side, */
/* close() the socket descriptor. NOTE: In */
/* a real application, the program would */
/* probably continue on to another unit of */
/* work with this socket. */
/****************************************/
close(sockfd);
}

Examples: UDP connectionless client/server introduction


The connectionless protocol server and client examples illustrate the socket APIs
that are written for User Datagram Protocol (UDP).

The server example and client example use the following sequence of function
calls:
v socket()
v bind()

94 Socket programming
The following figure illustrates the client/server relationship of the socket APIs for
a connectionless protocol.

Examples: Connecting a UDP server and client


The following examples show how to use UDP to connect a server to a
connectionless client, and a connectless client to a server.

Example: Connecting a UDP server to a client

The following example shows how to use UDP to connect a connectionless socket
server program to a client.
/***********************************************/
/* Header files needed to use the sockets API. */
/* File contain Macro, Data Type and Structure */
/* definitions along with Function prototypes. */
/***********************************************/
/* in qcle/h file */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* in qsysinc library */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/* Server's port number */


#define SERVPORT 3555

void main()
{
/***************************************/
/* Variable and structure definitions. */
/***************************************/
int sd, rc;

Chapter 7. Examples: Using sockets 95


struct sockaddr_in serveraddr, clientaddr;
int clientaddrlen = sizeof(clientaddr);
int serveraddrlen = sizeof(serveraddr);
char buffer[100];
char *bufptr = buffer;
int buflen = sizeof(buffer);

/******************************************/
/* The socket() function returns a socket */
/* descriptor representing an endpoint. */
/* The statement also identifies that the */
/* INET (Internet Protocol) address family */
/* with the UDP transport (SOCK_DGRAM) will */
/* be used for this socket. */
/******************************************/
/* get a socket descriptor */
if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket() failed");
exit(-1);
}

/********************************************/
/* After the socket descriptor is received, */
/* a bind() is done to assign a unique name */
/* to the socket. In this example, the user */
/* set the s_addr to zero. This allows the */
/* system to connect to any client that uses */
/* port 3555. */
/********************************************/
/* bind to address */
memset(&serveraddr, 0x00, serveraddrlen));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVPORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
if ((rc = bind(sd,
(struct sockaddr *)&serveraddr,
serveraddrlen)) < 0)
{
perror("bind() failed");
close(sd);
exit(-1);
}

/*********************************************/
/ Use the recvfrom() function to receive the */
/* data. The recvfrom() function waits */
/* indefinitely for data to arrive. */
/*********************************************/
/************************************************/
/* This example does not use flags that control */
/* the reception of the data. */
/************************************************/
/* Wait on client requests. */
rc = recvfrom(sd, bufptr, buflen, 0,
(struct sockaddr *)&clientaddr,
&clientaddrlen);
if (rc < 0)
{
perror("recvfrom() failed");
close(sd);
exit(-1);
}

printf("server received the following: <%s>\n",


bufptr);
printf(" from port %d and address %s.\n",

96 Socket programming
ntohs(clientaddr.sin_port),
inet_ntoa(clientaddr.sin_addr));

/************************************************/
/* Send a reply by using the sendto() function. */
/* In this example, the system echoes the received */
/* data back to the client. */
/************************************************/
/************************************************/
/* This example does not use flags that control */
/* the transmission of the data */
/************************************************/
/* Send a reply, just echo the request */
rc = sendto(sd, bufptr, buflen, 0,
(struct sockaddr *)&clientaddr,
clientaddrlen);
if (rc < 0)
{
perror("sendto() failed");
close(sd);
exit(-1);
}

/********************************************/
/* When the data has been sent, close() the */
/* socket descriptor. */
/********************************************/
/* Close() the socket descriptor. */
close(sd);
exit(0);
} /* end of main() */

Example: Connecting a UDP client to a server

The following example shows how to use UDP to connect a connectionless socket
client program to a server.
/***********************************************/
/* Header files needed to use the sockets API. */
/* File contain Macro, Data Type and Structure */
/* definitions along with Function prototypes. */
/***********************************************/
/* in qcle/h file */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* in qsysinc library */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

/* Host name of my system */


#define SERVER "SERVERHOSTNAME"

/* Server's port number */


#define SERVPORT 3555

/* Pass in 1 parameter which is either the */


/* address or host name of the server, or */
/* set the server name in the #define */
/* SERVER. */
void main(int argc, char *argv[])
{
/***************************************/

Chapter 7. Examples: Using sockets 97


/* Variable and structure definitions. */
/***************************************/
int sd, rc;
struct sockaddr_in serveraddr;
int serveraddrlen = sizeof(serveraddr);
char server[255];
char buffer[100];
char *bufptr = buffer;
int buflen = sizeof(buffer);
struct hostent *hostp;

memset(buffer, 0x00, sizeof(buffer));


memcpy(buffer, "A CLIENT REQUEST", 16);

/******************************************/
/* The socket() function returns a socket */
/* descriptor representing an endpoint. */
/* The statement also identifies that the */
/* INET (Internet Protocol) address family */
/* with the UDP transport (SOCK_DGRAM) will */
/* be used for this socket. */
/******************************************/
/* get a socket descriptor */
if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket() failed");
exit(-1);
}

if (argc > 1)
strcpy(server, argv[1]);
else
strcpy(server, SERVER);

memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));


serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVPORT);
if ((serveraddr.sin_addr.s_addr = inet_addr(server))
== (unsigned long)INADDR_NONE)
{
/************************************************/
/* Use the gethostbyname() function to retrieve */
/* the address of the host server if the system */
/* passed the host name of the server as a parameter. */
/************************************************/
/* get server address */
hostp = gethostbyname(server);
if (hostp == (struct hostent *)NULL)
{
printf("HOST NOT FOUND --> ");
/* h_errno is usually defined */
/* in netdb.h */
printf("h_errno = %d\n",h_errno);
exit(-1);
}
memcpy(&serveraddr.sin_addr,
hostp->h_addr,
sizeof(serveraddr.sin_addr));
}

/**********************************************/
/* Use the sendto() function to send the data */
/* to the server. */
/*********************************************/
/************************************************/
/* This example does not use flags that control */
/* the transmission of the data. */

98 Socket programming
/************************************************/
/* send request to server */
rc = sendto(sd, bufptr, buflen, 0,
(struct sockaddr *)&serveraddr,
sizeof(serveraddr));
if (rc < 0)
{
perror("sendto() failed");
close(sd);
exit(-1);
}

/**********************************************/
/* Use the recvfrom() function to receive the */
/* data back from the server. */
/**********************************************/
/************************************************/
/* This example does not use flags that control */
/* the reception of the data. */
/************************************************/
/* Read server reply. */
/* Note: serveraddr is reset on the */
/* recvfrom() function. */
rc = recvfrom(sd, bufptr, buflen, 0,
(struct sockaddr *)&serveraddr,
&serveraddrlen);
if (rc < 0)
{
perror("recvfrom() failed");
close(sd);
exit(-1);
}

printf("client received the following: <%s>\n",


bufptr);
printf(" from port %d, from address %s\n",
ntohs(serveraddr.sin_port),
inet_ntoa(serveraddr.sin_addr));

/********************************************/
/* When the data has been received, close() */
/* the socket descriptor. */
/********************************************/
/* close() the socket descriptor. */
close(sd);
exit(0);
}

Examples: Connection-oriented server designs introduction


There are a number of ways that you can design a connection-oriented socket
server on the AS/400. While additional socket server designs are possible, the
designs provided in the examples below are the most common:
v Iterative server
In the iterative server example, a single server job handles all incoming
connections and all data flows with the client jobs. When the accept() API
completes, the server handles the entire transaction. This is the easiest server to
develop, but it does have a few problems. While the server is handling the
request from a given client, additional clients could be trying to get to the server.
These requests fill the listen() backlog and some of the them will be rejected
eventually.

Chapter 7. Examples: Using sockets 99


All of the remaining examples are concurrent server designs. In these designs,
the system uses multiple jobs and threads to handle the incoming connection
requests. With a concurrent server there are usually multiple clients that connect
to the server at the same time.
v spawn() server and spawn() worker
The spawn() server and spawn() worker example uses the spawn() API to create
a new job to handle each incoming request. After spawn() completes, the server
can then wait on the accept() API for the next incoming connection to be
received. The only problem with this server design is the performance overhead
of creating a new job each time a connection is received.
You can avoid the performance overhead of the spawn() server example by
using prestarted jobs. Instead of creating a new job each time a connection is
received, the incoming connection is given to a job that is already active. All of
the remaining examples in this topic use prestarted jobs.
v givedescriptor() server and takedescriptor() worker
The givedescriptor() server and takedescriptor() worker example uses the
AS/400-specific givedescriptor() and takedescriptor() APIs. These APIs pass the
incoming connection to the worker (client) job. The server prestarted all of the
worker jobs when the server job first started.
v sendmsg() server and recvmsg() worker
The sendmsg() server and recvmsg() worker example is nearly identical to the
givedescriptor() server and takedescriptor() worker examples. The following are
the main differences between the APIs:
– The system does not create an AS/400-specific data queue in this example.
– The system uses the industry standard sendmsg() and recvmsg() APIs to pass
the incoming connection from the server to the worker job.
– The performance of the sendmsg() and recvmsg() APIs is slightly better than
the performance of the givedescriptor() and takedescriptor() APIs.
v Multiple accept() servers and multiple accept() workers
In the previous examples, the worker job did not get involved until after the
server received the incoming connection request. The multiple accept() servers
and multiple accept() workers example of the system turns each of the worker
jobs into an iterative server. The server job still calls the socket(), bind(), and
listen() APIs. When the listen() call completes, the server creates each of the
worker jobs and gives a listening socket to each one of them. All of the worker
jobs then call the accept() API. When a client tries to connect to the server, only
one accept() call completes, and that worker handles the connection. This type of
design removes the need to give the incoming connection to a worker job, and
saves the performance overhead that is associated with that operation. As a
result, this design has the best performance.
A worker job or a worker thread refers to a process or subprocess (thread) that
does data processing by using the socket descriptor. For example, a worker
process accesses a database file to extract and format information for sending to
the remote peer through the socket descriptor and its associated connection. It
could then receive a response or set of data from the remote peer and update
the database accordingly.
Depending on the design of the server, the worker usually does not perform the
connection ″bring-up″ or initiation. This is usually done by the listening or
server job or thread. The listening or server job usually passes the descriptor to
the worker job or thread.

100 Socket programming


Notes:
1. In the example server programs, the number of incoming connections that
the server allows depends on the first parameter that is passed to the server.
The default is for the server to allow only one connection.
2. All of the five server example programs use the same port number (12345).
This means that only one of the server programs can be active at any given
time. The system used the same port on all the server programs so that it
could use a single client example program with all of the servers.

Example: Writing an iterative server program


This example shows how you can write an iterative server program. The figure
illustrates how the server and client jobs interact when the system used the
iterative server design.

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SERVER_PORT 12345

main (int argc, char *argv[])


{
int i, len, num, rc;
int listen_sd, accept_sd;
char buffer[80];
struct sockaddr_in addr;

/*************************************************/
/* If an argument was specified, use it to */

Chapter 7. Examples: Using sockets 101


/* control the number of incoming connections */
/*************************************************/
if (argc >= 2)
num = atoi(argv[1]);
else
num = 1;

/*************************************************/
/* Create an AF_INET stream socket to receive */
/* incoming connections on */
/*************************************************/
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}

/*************************************************/
/* Bind the socket */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);
rc = bind(listen_sd,
(struct sockaddr *)&addr, sizeof(addr));
if (rc < 0)
{
perror("bind() failed");
close(listen_sd);
exit(-1);
}

/*************************************************/
/* Set the listen backlog */
/*************************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}

/*************************************************/
/* Inform the user that the server is ready */
/*************************************************/
printf("The server is ready\n");

/*************************************************/
/* Go through the loop once for each connection */
/*************************************************/
for (i=0; i < num; i++)
{
/**********************************************/
/* Wait for an incoming connection */
/**********************************************/
printf("Iteration: %d\n", i+1);
printf(" waiting on accept()\n");
accept_sd = accept(listen_sd, NULL, NULL);
if (accept_sd < 0)
{
perror("accept() failed");
close(listen_sd);
exit(-1);
}

102 Socket programming


printf(" accept completed successfully\n");

/**********************************************/
/* Receive a message from the client */
/**********************************************/
printf(" wait for client to send us a message\n");
rc = recv(accept_sd, buffer, sizeof(buffer), 0);
if (rc <= 0)
{
perror("recv() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}
printf(" <%s>\n", buffer);

/**********************************************/
/* Echo the data back to the client */
/**********************************************/
printf(" echo it back\n");
len = rc;
rc = send(accept_sd, buffer, len, 0);
if (rc <= 0)
{
perror("send() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}

/**********************************************/
/* Close the incoming connection */
/**********************************************/
close(accept_sd);
}

/*************************************************/
/* Close the listen socket */
/*************************************************/
close(listen_sd);
}

Examples: Using the spawn() API to create child processes


The server example shows how a server program can use the spawn() API to
create a child process that inherits the socket descriptor from the parent. The server
job waits for an incoming connection, and then calls spawn() to create children
jobs to handle the incoming connection. The attributes that are inherited by the
child with the spawn() function include the following items:
v The socket and file descriptors
v The signal mask
v The signal action vector
v The environment variables

Chapter 7. Examples: Using sockets 103


The following figure illustrates how the server, worker, and client jobs interact
when the spawn() server design is used.

For more information, go to the spawn() function details in the Sockets APIs.

The worker example contains the code for the worker job that is created by
spawn(). The worker job receives a data buffer from the client job and echos it
back.

Example: Using the spawn() API to create child processes

This example shows how to use the spawn() API to create a child process that
inherits the socket descriptor from the parent.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <spawn.h>

#define SERVER_PORT 12345

main (int argc, char *argv[])


{
int i, num, pid, rc;
int listen_sd, accept_sd;
char *spawn_argv[3];
char *spawn_envp[1];
char buffer[80];

104 Socket programming


struct inheritance inherit;
struct sockaddr_in addr;

/*************************************************/
/* If an argument was specified, use it to */
/* control the number of incoming connections */
/*************************************************/
if (argc >= 2)
num = atoi(argv[1]);
else
num = 1;

/*************************************************/
/* Create an AF_INET stream socket to receive */
/* incoming connections on */
/*************************************************/
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}

/*************************************************/
/* Bind the socket */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
rc = bind(listen_sd,
(struct sockaddr *)&addr, sizeof(addr));
if (rc < 0)
{
perror("bind() failed");
close(listen_sd);
exit(-1);
}

/*************************************************/
/* Set the listen backlog */
/*************************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}

/*************************************************/
/* Inform the user that the server is ready */
/*************************************************/
printf("The server is ready\n");

/*************************************************/
/* Go through the loop once for each connection */
/*************************************************/
for (i=0; i < num; i++)
{
/**********************************************/
/* Wait for an incoming connection */
/**********************************************/
printf("Iteration: %d\n", i+1);
printf(" waiting on accept()\n");
accept_sd = accept(listen_sd, NULL, NULL);
if (accept_sd < 0)

Chapter 7. Examples: Using sockets 105


{
perror("accept() failed");
close(listen_sd);
exit(-1);
}
printf(" accept completed successfully\n");

/**********************************************/
/* Initialize the spawn parameters */
/* */
/* Turn the socket descriptor for the new */
/* connection into a string and pass it to */
/* the child program as an argument. */
/**********************************************/
memset(&inherit, 0, sizeof(inherit));
sprintf(buffer, "%d", accept_sd);
spawn_argv[0] = "";
spawn_argv[1] = buffer;
spawn_argv[2] = NULL;
spawn_envp[0] = NULL;

/**********************************************/
/* Create the worker job */
/**********************************************/
printf(" creating worker job\n");
pid = spawn("/QSYS.LIB/QGPL.LIB/WRKR1.PGM",
accept_sd + 1, NULL, &inherit,
spawn_argv, spawn_envp);
if (pid < 0)
{
perror("spawn() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}
printf(" spawn completed successfully\n");

/**********************************************/
/* Close the incoming connection since */
/* it has been given to a worker to handle */
/**********************************************/
close(accept_sd);
}

/*************************************************/
/* Close the listen socket */
/*************************************************/
close(listen_sd);
}

Example: Enabling the worker job to receive a data buffer

This example contains the code that enables the worker job to receive a data buffer
from the client job and echo it back.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>

main (int argc, char *argv[])


{
int rc, len;
int sockfd;
char buffer[80];

/*************************************************/
/* The descriptor for the incoming connection is */

106 Socket programming


/* passed to this worker job as a command line */
/* parameter */
/*************************************************/
sockfd = atoi(argv[1]);

/*************************************************/
/* Receive a message from the client */
/*************************************************/
printf("Wait for client to send us a message\n");
rc = recv(sockfd, buffer, sizeof(buffer), 0);
if (rc <= 0)
{
perror("recv() failed");
close(sockfd);
exit(-1);
}
printf("<%s>\n", buffer);

/*************************************************/
/* Echo the data back to the client */
/*************************************************/
printf("Echo it back\n");
len = rc;
rc = send(sockfd, buffer, len, 0);
if (rc <= 0)
{
perror("send() failed");
close(sockfd);
exit(-1);
}

/*************************************************/
/* Close the incoming connection */
/*************************************************/
close(sockfd);
}

Examples: Using givedescriptor() and takedescriptor() APIs to


handle incoming connections
The givedescriptor() and takedescriptor() examples show how to design a server
program that uses these APIs to handle incoming connections. When the server
starts, it creates a pool of worker jobs. These preallocated (spawned) worker jobs
wait until needed. When the client job connects to the server, the server gives the
incoming connection to one of the worker jobs.

Chapter 7. Examples: Using sockets 107


The following figure illustrates how the server, worker, and client jobs interact
when the system uses the givedescriptor() and takedescriptor() server design.

Example: Using the givedescriptor() API to handle incoming connections

This examples show how to design the givedescriptor() server API to handle
incoming connections and to create a pool of worker jobs.

Note:

108 Socket programming


#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <spawn.h>
#include <qusec.h>
#include <qclrdtaq.h>
#include <qrcvdtaq.h>

#define ID_LEN 16
#define SERVER_PORT 12345

main (int argc, char *argv[])


{
int i, num, pid, rc;
int listen_sd, accept_sd;
char *spawn_argv[2];
char *spawn_envp[1];
char jobid[ID_LEN];
struct inheritance inherit;
struct sockaddr_in addr;
_Decimal(5,0) jobid_len = ID_LEN;

/*************************************************/
/* If an argument is specified, use it to */
/* control the number of incoming connections */
/*************************************************/
if (argc >= 2)
num = atoi(argv[1]);
else
num = 1;

/*************************************************/
/* Create an AF_INET stream socket to receive */
/* incoming connections on */
/*************************************************/
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}

/*************************************************/
/* Bind the socket */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);
rc = bind(listen_sd,
(struct sockaddr *)&addr, sizeof(addr));
if (rc < 0)
{
perror("bind() failed");
close(listen_sd);
exit(-1);
}

/*************************************************/
/* Set the listen backlog */
/*************************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);

Chapter 7. Examples: Using sockets 109


exit(-1);
}

/*************************************************/
/* Create and clear a data queue */
/*************************************************/
system("CRTDTAQ DTAQ(QUSRSYS/DQ) MAXLEN(20)");

QCLRDTAQ("DQ ", /* dtaq name */


"QUSRSYS "); /* dtaq lib */

/*************************************************/
/* Initialize spawn parms prior to entering the */
/* for loop */
/*************************************************/
memset(&inherit, 0, sizeof(inherit));
spawn_argv[0] = "";
spawn_argv[1] = NULL;
spawn_envp[0] = NULL;

/*************************************************/
/* Create each of the worker jobs */
/*************************************************/
printf("Creating worker jobs...\n");
for (i=0; i < num; i++)
{
pid = spawn("/QSYS.LIB/QGPL.LIB/WRKR2.PGM",
-1, NULL, &inherit,
spawn_argv, spawn_envp);
if (pid < 0)
{
perror("spawn() failed");
close(listen_sd);
exit(-1);
}
printf(" Worker = %d\n", pid);
}

/*************************************************/
/* Inform the user that the server is ready */
/*************************************************/
printf("The server is ready\n");

/*************************************************/
/* Go through the loop once for each connection */
/*************************************************/
for (i=0; i < num; i++)
{
/**********************************************/
/* Wait for an incoming connection */
/**********************************************/
printf("Iteration: %d\n", i+1);
printf(" waiting on accept()\n");
accept_sd = accept(listen_sd, NULL, NULL);
if (accept_sd < 0)
{
perror("accept() failed");
close(listen_sd);
exit(-1);
}
printf(" accept completed successfully\n");

/**********************************************/
/* Receive the job ID of the next available */
/* worker from the data queue */
/**********************************************/
QRCVDTAQ("DQ ", /* dtaq name */

110 Socket programming


"QUSRSYS ", /* dtaq lib */
&jobid_len, /* length of data */
jobid, /* data */
10); /* wait time (sec) */
if (jobid_len != ID_LEN)
{
printf("QRCVDTAQ failed\n");
close(listen_sd);
close(accept_sd);
exit(-1);
}
printf(" QRCVDTAQ completed successfully\n");

/**********************************************/
/* Use the job ID that was dequeued from the */
/* data queue to give the descriptor for the */
/* incoming connection to the worker job that */
/* is waiting for the takedescriptor() API. */
/**********************************************/
rc = givedescriptor(accept_sd, jobid);
if (rc < 0)
{
perror("givedescriptor() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}
printf(" givedescriptor completed successfully\n");

/**********************************************/
/* Close the incoming connection since */
/* it has been given to a worker to handle */
/**********************************************/
close(accept_sd);
}

/*************************************************/
/* Close the server and listen sockets */
/*************************************************/
close(listen_sd);
}

Example: Using the takedescriptor() API to receive worker jobs

This example shows how the takedescriptor() client job connects to the server and
receives the worker jobs.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <qusec.h>
#include <qusrjobi.h>
#include <qsnddtaq.h>

#define ID_LEN 16

main (int argc, char *argv[])


{
int rc, len;
int sockfd;
char buffer[80];
char jobid[ID_LEN];
_Decimal(5,0) jobid_len = ID_LEN;

/* place to put output */


struct Qwc_JOBI0100 jobinfo;

Chapter 7. Examples: Using sockets 111


/* error structure */
Qus_EC_t error = { sizeof(Qus_EC_t) };

/*************************************************/
/* Determine this job's internal job ID */
/*************************************************/
QUSRJOBI(&jobinfo,
sizeof(jobinfo),
"JOBI0100",
"* ",
" ",
&error);
if (error.Bytes_Available != 0)
{
printf("QUSRJOBI() failed\n");
exit(-1);
}

/*************************************************/
/* Copy the job information into local variable */
/*************************************************/
memcpy(jobid, jobinfo.Int_Job_ID, 16);

/*************************************************/
/* Enqueue the internal job ID onto the shared */
/* data queue */
/*************************************************/
QSNDDTAQ("DQ ", /* dtaq name */
"QUSRSYS ", /* dtaq lib */
ID_LEN, /* length of data */
jobid); /* data */

/*************************************************/
/* Issue the takedescriptor() API which will */
/* block until the server job gives its */
/* descriptor to this job. */
/*************************************************/
printf("Waiting on takedescriptor\n");
sockfd = takedescriptor(NULL);
if (sockfd < 0)
{
perror("takedescriptor() failed");
exit(-1);
}

/*************************************************/
/* Receive a message from the client */
/*************************************************/
printf("Wait for client to send us a message\n");
rc = recv(sockfd, buffer, sizeof(buffer), 0);
if (rc <= 0)
{
perror("recv() failed");
close(sockfd);
exit(-1);
}
printf("<%s>\n", buffer);

/*************************************************/
/* Echo the data back to the client */
/*************************************************/
printf("Echo it back\n");
len = rc;
rc = send(sockfd, buffer, len, 0);
if (rc <= 0)
{
perror("send() failed");

112 Socket programming


close(sockfd);
exit(-1);
}

/*************************************************/
/* Close the descriptors */
/*************************************************/
close(sockfd);
}

Examples: Using sendmsg() and recvmsg() APIs to handle


incoming connections
The sendmsg() and recvmsg() examples show how to design a server program that
uses these APIs to handle incoming connections. When the server starts, it creates a
pool of worker jobs. These preallocated (spawned) worker jobs wait until needed.
When the client job connects to the server, the server gives the incoming
connection to one of the worker jobs.

The following figure illustrates how the server, worker, and client jobs interact
when the system uses the sendmsg() and recvmsg() server design.

Chapter 7. Examples: Using sockets 113


Example: Using the sendmsg() API to create worker jobs

This example shows how to use the sendmsg() API to create a pool of worker jobs.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <spawn.h>

#define SERVER_PORT 12345

main (int argc, char *argv[])


{
int i, num, pid, rc;
int listen_sd, accept_sd;
int server_sd, worker_sd, pair_sd[2];
char *spawn_argv[3];
char *spawn_envp[1];
char buffer[80];
struct inheritance inherit;
struct msghdr msg;
struct sockaddr_in addr;

/*************************************************/
/* If an argument is specified, use it to */
/* control the number of incoming connections */
/*************************************************/
if (argc >= 2)
num = atoi(argv[1]);
else
num = 1;

/*************************************************/
/* Create an AF_INET stream socket to receive */
/* incoming connections on */
/*************************************************/
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}

/*************************************************/
/* Bind the socket */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);
rc = bind(listen_sd,
(struct sockaddr *)&addr, sizeof(addr));
if (rc < 0)
{
perror("bind() failed");
close(listen_sd);
exit(-1);
}

/*************************************************/
/* Set the listen backlog */
/*************************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");

114 Socket programming


close(listen_sd);
exit(-1);
}

/*************************************************/
/* Create a pair of UNIX datagram sockets */
/*************************************************/
rc = socketpair(AF_UNIX, SOCK_DGRAM, 0, pair_sd);
if (rc != 0)
{
perror("socketpair() failed");
close(listen_sd);
exit(-1);
}
server_sd = pair_sd[0];
worker_sd = pair_sd[1];

/*************************************************/
/* Initialize parameters prior to entering */
/* command for loop */
/* */
/* Turn the worker socket descriptor into a */
/* string and pass it to the child program as */
/* an argument. */
/*************************************************/
memset(&inherit, 0, sizeof(inherit));
sprintf(buffer, "%d", worker_sd);
spawn_argv[0] = "";
spawn_argv[1] = buffer;
spawn_argv[2] = NULL;
spawn_envp[0] = NULL;

/*************************************************/
/* Create each of the worker jobs */
/*************************************************/
printf("Creating worker jobs...\n");
for (i=0; i < num; i++)
{
pid = spawn("/QSYS.LIB/QGPL.LIB/WRKR3.PGM",
worker_sd + 1, NULL, &inherit,
spawn_argv, spawn_envp);
if (pid < 0)
{
perror("spawn() failed");
close(listen_sd);
close(server_sd);
close(worker_sd);
exit(-1);
}
printf(" Worker = %d\n", pid);
}

/*************************************************/
/* Close the worker side of the socketpair */
/*************************************************/
close(worker_sd);

/*************************************************/
/* Inform the user that the server is ready */
/*************************************************/
printf("The server is ready\n");

/*************************************************/
/* Go through the loop once for each connection */
/*************************************************/
for (i=0; i < num; i++)
{

Chapter 7. Examples: Using sockets 115


/**********************************************/
/* Wait for an incoming connection */
/**********************************************/
printf("Iteration: %d\n", i+1);
printf(" waiting on accept()\n");
accept_sd = accept(listen_sd, NULL, NULL);
if (accept_sd < 0)
{
perror("accept() failed");
close(listen_sd);
close(server_sd);
exit(-1);
}
printf(" accept completed successfully\n");

/**********************************************/
/* Initialize message header structure */
/**********************************************/
memset(&msg, 0, sizeof(msg));

/**********************************************/
/* You are not sending any data so you do not */
/* need to set either of the msg_iov fields. */
/* The memset of the message header structure */
/* will set the msg_iov pointer to NULL and */
/* it will set the msg_iovcnt field to 0. */
/**********************************************/

/**********************************************/
/* The only fields in the message header */
/* structure that need to be filled in are */
/* the msg_accrights fields. */
/**********************************************/
msg.msg_accrights = (char *)&accept_sd
msg.msg_accrightslen = sizeof(accept_sd);

/**********************************************/
/* Give the incoming connection to one of the */
/* worker jobs. */
/* */
/* NOTE: It is not known which worker job will*/
/* get this inbound connection. */
/**********************************************/
rc = sendmsg(server_sd, &msg, 0);
if (rc < 0)
{
perror("sendmsg() failed");
close(listen_sd);
close(accept_sd);
close(server_sd);
exit(-1);
}
printf(" sendmsg completed successfully\n");

/**********************************************/
/* Close the incoming connection since */
/* it has been given to a worker to handle */
/**********************************************/
close(accept_sd);
}

/*************************************************/
/* Close the server and listen sockets */
/*************************************************/
close(server_sd);
close(listen_sd);
}

116 Socket programming


Example: Using the recvmsg() API to receive worker jobs

This example shows how to use the recvmsg() API client job to receive the worker
jobs.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>

main (int argc, char *argv[])


{
int rc, len;
int worker_sd, pass_sd;
char buffer[80];
struct iovec iov[1];
struct msghdr msg;

/*************************************************/
/* One of the socket descriptors that is */
/* returned by socketpair() is passed to this */
/* worker job as a command line parameter */
/*************************************************/
worker_sd = atoi(argv[1]);

/*************************************************/
/* Initialize message header structure */
/*************************************************/
memset(&msg, 0, sizeof(msg));
memset(iov, 0, sizeof(iov));

/*************************************************/
/* The recvmsg() call will not block unless a */
/* non-zero length data buffer is specified */
/*************************************************/
iov[0].iov_base = buffer;
iov[0].iov_len = sizeof(buffer);
msg.msg_iov = iov;
msg.msg_iovlen = 1;

/*************************************************/
/* Fill in the msg_accrights fields so that you */
/* can receive the descriptor */
/*************************************************/
msg.msg_accrights = (char *)&pass_sd
msg.msg_accrightslen = sizeof(pass_sd);

/*************************************************/
/* Wait for the descriptor to arrive */
/*************************************************/
printf("Waiting on recvmsg\n");
rc = recvmsg(worker_sd, &msg, 0);
if (rc < 0)
{
perror("recvmsg() failed");
close(worker_sd);
exit(-1);
}
else if (msg.msg_accrightslen <= 0)
{
printf("Descriptor was not received\n");
close(worker_sd);
exit(-1);
}
else
{
printf("Received descriptor = %d\n", pass_sd);
}

Chapter 7. Examples: Using sockets 117


/*************************************************/
/* Receive a message from the client */
/*************************************************/
printf("Wait for client to send us a message\n");
rc = recv(pass_sd, buffer, sizeof(buffer), 0);
if (rc <= 0)
{
perror("recv() failed");
close(worker_sd);
close(pass_sd);
exit(-1);
}
printf("<%s>\n", buffer);

/*************************************************/
/* Echo the data back to the client */
/*************************************************/
printf("Echo it back\n");
len = rc;
rc = send(pass_sd, buffer, len, 0);
if (rc <= 0)
{
perror("send() failed");
close(worker_sd);
close(pass_sd);
exit(-1);
}

/*************************************************/
/* Close the descriptors */
/*************************************************/
close(worker_sd);
close(pass_sd);
}

Examples: Using multiple accept() APIs to handle incoming


connection requests
These examples show how to design a server program that uses the multiple
accept() model for handling incoming connection requests. When the multiple
accept() server starts up, it does a socket(), bind(), and listen() as normal. It then
creates a pool of worker jobs and gives each worker job the listening socket. Each
multiple accept() worker then calls accept().

118 Socket programming


The following figure illustrates how the server, worker, and client jobs interact
when the system uses the multiple accept() server design.

Example: Using multiple accept() APIs to create worker jobs

This example shows how to use the multiple accept() model to create a pool of
worker jobs.
#include <stdio.h>
#include <stdlib.h>
#include sys/socket.h>
#include <netinet/in.h>
#include <spawn.h>

#define SERVER_PORT 12345

main (int argc, char *argv[])


{
int i, num, pid, rc;
int listen_sd, accept_sd;
char *spawn_argv[3];
char *spawn_envp[1];
char buffer[80];
struct inheritance inherit;
struct sockaddr_in addr;

/*************************************************/
/* If an argument is specified, use it to */
/* control the number of incoming connections */
/*************************************************/
if (argc >= 2)
num = atoi(argv[1]);

Chapter 7. Examples: Using sockets 119


else
num = 1;

/*************************************************/
/* Create an AF_INET stream socket to receive */
/* incoming connections on */
/*************************************************/
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}

/*************************************************/
/* Bind the socket */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);
rc = bind(listen_sd,
(struct sockaddr *)&addr, sizeof(addr));
if (rc < 0)
{
perror("bind() failed");
close(listen_sd);
exit(-1);
}

/*************************************************/
/* Set the listen backlog */
/*************************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}

/*************************************************/
/* Initialize parameters prior to entering the */
/* command for loop */
/* */
/* Turn the listen socket descriptor into a */
/* string and pass it to the child program as */
/* an argument. */
/*************************************************/
memset(&inherit, 0, sizeof(inherit));
sprintf(buffer, "%d", listen_sd);
spawn_argv[0] = "";
spawn_argv[1] = buffer;
spawn_argv[2] = NULL;
spawn_envp[0] = NULL;

/*************************************************/
/* Create each of the worker jobs */
/*************************************************/
printf("Creating worker jobs...\n");
for (i=0; i < num; i++)
{
pid = spawn("/QSYS.LIB/QGPL.LIB/WRKR4.PGM",
listen_sd + 1, NULL, &inherit,
spawn_argv, spawn_envp);
if (pid < 0)
{

120 Socket programming


perror("spawn() failed");
close(listen_sd);
exit(-1);
}
printf(" Worker = %d\n", pid);
}

/*************************************************/
/* Inform the user that the server is ready */
/*************************************************/
printf("The server is ready\n");

/*************************************************/
/* Close the listening socket */
/*************************************************/
close(listen_sd);
}

Example: Using multiple accept() APIs to receive worker jobs

This example shows how multiple accept() APIs receive the worker jobs and call
the accept() server.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>

main (int argc, char *argv[])


{
int rc, len;
int listen_sd, accept_sd;
char buffer[80];

/*************************************************/
/* The listen socket descriptor is passed to */
/* this worker job as a command line parameter */
/*************************************************/
listen_sd = atoi(argv[1]);

/*************************************************/
/* Wait for an incoming connection */
/*************************************************/
printf("Waiting on accept()\n");
accept_sd = accept(listen_sd, NULL, NULL);
if (accept_sd < 0)
{
perror("accept() failed");
close(listen_sd);
exit(-1);
}
printf("Accept completed successfully\n");

/*************************************************/
/* Receive a message from the client */
/*************************************************/
printf("Wait for client to send us a message\n");
rc = recv(accept_sd, buffer, sizeof(buffer), 0);
if (rc <= 0)
{
perror("recv() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}
printf("<%s>\n", buffer);

/*************************************************/

Chapter 7. Examples: Using sockets 121


/* Echo the data back to the client */
/*************************************************/
printf("Echo it back\n");
len = rc;
rc = send(accept_sd, buffer, len, 0);
if (rc <= 0)
{
perror("send() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}

/*************************************************/
/* Close the descriptors */
/*************************************************/
close(listen_sd);
close(accept_sd);
}

Example: Code for the connection-oriented common client


This example provides the code for the client job. The client job does a socket(),
connect(), send(), recv(), and close(). The client job is not aware that the data
buffer it sent and received is going to a worker job rather than to the server.

This client job works with all five types of servers that are listed in the
connection-oriented server designs examples.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SERVER_PORT 12345

main (int argc, char *argv[])


{
int len, rc;
int sockfd;
char send_buf[80];
char recv_buf[80];
struct sockaddr_in addr;

/*************************************************/
/* Create an AF_INET stream socket */
/*************************************************/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket");
exit(-1);
}

/*************************************************/
/* Initialize the socket address structure */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);

/*************************************************/
/* Connect to the server */
/*************************************************/
rc = connect(sockfd,

122 Socket programming


(struct sockaddr *)&addr,
sizeof(struct sockaddr_in));
if (rc < 0)
{
perror("connect");
close(sockfd);
exit(-1);
}
printf("Connect completed.\n");

/*************************************************/
/* Enter data buffer that is to be sent */
/*************************************************/
printf("Enter message to be sent:\n");
gets(send_buf);

/*************************************************/
/* Send data buffer to the worker job */
/*************************************************/
len = send(sockfd, send_buf, strlen(send_buf) + 1, 0);
if (len != strlen(send_buf) + 1)
{
perror("send");
close(sockfd);
exit(-1);
}
printf("%d bytes sent\n", len);

/*************************************************/
/* Receive data buffer from the worker job */
/*************************************************/
len = recv(sockfd, recv_buf, sizeof(recv_buf), 0);
if (len != strlen(send_buf) + 1)
{
perror("recv");
close(sockfd);
exit(-1);
}
printf("%d bytes received\n", len);

/*************************************************/
/* Close the socket */
/*************************************************/
close(sockfd);
}

Example: Threadsafe network routines for using gethostbyaddr_r()


Following is an example of a program that uses gethostbyaddr_r(). All other
routines with names that end in ″_r″ have similar semantics and are also
threadsafe. This example program takes an IP address in the dotted-decimal
notation and prints the host name.

/********************************************************/
/* Header files */
/********************************************************/
#include </netdb.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define HEX00 '\x00'
#define NUMPARMS 2

Chapter 7. Examples: Using sockets 123


/********************************************************/
/* Pass one parameter that is the IP address in */
/* dotted decimal notation. The host name will be */
/* displayed if found; otherwise, a message states */
/* host not found. */
/********************************************************/
int main(int argc, char *argv[])
{
int rc;
struct in_addr internet_address;
struct hostent hst_ent;
struct hostent_data hst_ent_data;
char dotted_decimal_address [16];
char host_name[MAXHOSTNAMELEN];

/**********************************************************/
/* Verify correct number of arguments have been passed */
/**********************************************************/
if (argc != NUMPARMS)
{
printf("Wrong number of parms passed\n");
exit(-1);
}
/**********************************************************/
/* Obtain addressability to parameters passed */
/**********************************************************/
strcpy(dotted_decimal_address, argv[1]);

/**********************************************************/
/* Initialize the structure-field */
/* hostent_data.host_control_blk with hexadecimal zeros */
/* before its initial use. If you require compatibility */
/* with other platforms, then you must initialize the */
/* entire hostent_data structure with hexadecimal zeros. */
/**********************************************************/
/* Initialize to hex 00 hostent_data structure */
/**********************************************************/
memset(&hst_ent_data,HEX00,sizeof(struct hostent_data));

/**********************************************************/
/* Translate an Internet address from dotted decimal */
/* notation to 32-bit IP address format. */
/**********************************************************/
internet_address.s_addr=inet_addr(dotted_decimal_address);

/**********************************************************/
/* Obtain host name */
/**********************************************************/
/**********************************************************/
/* NOTE: The gethostbyaddr_r() returns an integer. */
/* The following are possible values: */
/* -1 (unsuccessful call) */
/* 0 (successful call) */
/**********************************************************/
if ((rc=gethostbyaddr_r((char *) &internet_address,
sizeof(struct in_addr), AF_INET,
&hst_ent, &hst_ent_data)) == -1)
{
printf("Host name not found\n");
exit(-1);
}
else
{
/*****************************************************/
/* Copy the host name to an output buffer */
/*****************************************************/
(void) memcpy((void *) host_name,

124 Socket programming


/****************************************************/
/* You must address all the results through the */
/* hostent structure hst_ent. */
/* NOTE: Hostent_data structure hst_ent_data is just */
/* a data repository that is used to support the */
/* hostent structure. Applications should consider */
/* hostent_data a storage area to put host level data */
/* that the application does not need to access. */
/****************************************************/
(void *) hst_ent.h_name,
MAXHOSTNAMELEN);
/*****************************************************/
/* Print the host name */
/*****************************************************/
printf("The host name is %s\n", host_name);

}
exit(0);
}

Examples: Sending and receiving a multicast datagram


IP multicasting provides the capability for an application to send a single IP
datagram that a group of hosts in a network can receive. The hosts that are in the
group may reside on a single subnet or may be on different subnets that connect
multicast-capable routers. Hosts may join and leave groups at any time. There are
no restrictions on the location or number of members in a host group. A class D
Internet address in the range 224.0.0.1 to 239.255.255.255 identifies a host group.

An application program can send or receive multicast datagrams by using the


socket() API and connectionless SOCK_DGRAM type sockets. Multicasting is a
one-to-many transmission method. You cannot use connection-oriented sockets of
type SOCK_STREAM for multicasting. When a socket of type SOCK_DGRAM is
created, an application can use the setsockopt() function to control the multicast
characteristics associated with that socket. The setsockopt() function accepts the
following IPPROTO_IP level flags:
v IP_ADD_MEMBERSHIP: Joins the multicast group specified.
v IP_DROP_MEMBERSHIP: Leaves the multicast group specified.
v IP_MULTICAST_IF: Sets the interface over which outgoing multicast datagrams
are sent.
v IP_MULTICAST_TTL: Sets the Time To Live (TTL) in the IP header for outgoing
multicast datagrams.
v IP_MULTICAST_LOOP: Specifies whether or not a copy of an outgoing
multicast datagram is delivered to the sending host as long as it is a member of
the multicast group.

The following examples enable a socket to send and receive multicast datagrams.
The steps needed to send a multicast datagram differ from the steps needed to
receive a multicast datagram.

Example: Sending a multicast datagram

The following example enables a socket to perform the steps listed below and to
send multicast datagrams:
1. Create an AF_INET, SOCK_DGRAM type socket.
2. Initialize a sockaddr_in structure with the destination group IP address and
port number.

Chapter 7. Examples: Using sockets 125


3. Set the IP_MULTICAST_LOOP socket option according to whether the sending
system should receive a copy of the multicast datagrams that are transmitted.
4. Set the IP_MULTICAST_IF socket option to define the local interface over
which you want to send the multicast datagrams.
5. Send the datagram.
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>

struct in_addr localInterface;


struct sockaddr_in groupSock;
int sd;
int datalen;
char databuf[1024];

int main (int argc, char *argv[])


{

/* ------------------------------------------------------------*/
/* */
/* Send Multicast Datagram code example. */
/* */
/* ------------------------------------------------------------*/

/*
* Create a datagram socket on which to send.
*/
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("opening datagram socket");
exit(1);
}

/*
* Initialize the group sockaddr structure with a
* group address of 225.1.1.1 and port 5555.
*/
memset((char *) &groupSock, 0, sizeof(groupSock));
groupSock.sin_family = AF_INET;
groupSock.sin_addr.s_addr = inet_addr("225.1.1.1");
groupSock.sin_port = htons(5555);

/*
* Disable loopback so you do not receive your own datagrams.
*/
{
char loopch=0;

if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP,


(char *)&loopch, sizeof(loopch)) < 0) {
perror("setting IP_MULTICAST_LOOP:");
close(sd);
exit(1);
}
}

/*
* Set local interface for outbound multicast datagrams.
* The IP address specified must be associated with a local,
* multicast-capable interface.
*/

126 Socket programming


localInterface.s_addr = inet_addr("9.5.1.1");
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&localInterface,
sizeof(localInterface)) < 0) {
perror("setting local interface");
exit(1);
}

/*
* Send a message to the multicast group specified by the
* groupSock sockaddr structure.
*/
datalen = 10;
if (sendto(sd, databuf, datalen, 0,
(struct sockaddr*)&groupSock,
sizeof(groupSock)) < 0)
{
perror("sending datagram message");
}
}

Example: Receiving a multicast datagram

The following example enables a socket to perform the steps listed below and to
receive multicast datagrams:
1. Create an AF_INET, SOCK_DGRAM type socket.
2. Set the SO_REUSEADDR option to allow multiple applications to receive
datagrams that are destined to the same local port number.
3. Use the bind() verb to specify the local port number. Specify the IP address as
INADDR_ANY in order to receive datagrams that are addressed to a multicast
group.
4. Use the IP_ADD_MEMBERSHIP socket option to join the multicast group that
receive the datagrams. When joining a group, specify the class D group address
along with the IP address of a local interface. The system must call the
IP_ADD_MEMBERSHIP socket option for each local interface receiving the
multicast datagrams.
5. Receive the datagram.
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>

struct sockaddr_in localSock;


struct ip_mreq group;
int sd;
int datalen;
char databuf[1024];

int main (int argc, char *argv[])


{

/* ------------------------------------------------------------*/
/* */
/* Receive Multicast Datagram code example. */
/* */
/* ------------------------------------------------------------*/

Chapter 7. Examples: Using sockets 127


/*
* Create a datagram socket on which to receive.
*/
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("opening datagram socket");
exit(1);
}

/*
* Enable SO_REUSEADDR to allow multiple instances of this
* application to receive copies of the multicast datagrams.
*/
{
int reuse=1;

if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,


(char *)&reuse, sizeof(reuse)) < 0) {
perror("setting SO_REUSEADDR");
close(sd);
exit(1);
}
}

/*
* Bind to the proper port number with the IP address
* specified as INADDR_ANY.
*/
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(5555);;
localSock.sin_addr.s_addr = INADDR_ANY;

if (bind(sd, (struct sockaddr*)&localSock, sizeof(localSock))) {


perror("binding datagram socket");
close(sd);
exit(1);
}

/*
* Join the multicast group 225.1.1.1 on the local 9.5.1.1
* interface. Note that this IP_ADD_MEMBERSHIP option must be
* called for each local interface over which the multicast
* datagrams are to be received.
*/
group.imr_multiaddr.s_addr = inet_addr("225.1.1.1");
group.imr_interface.s_addr = inet_addr("9.5.1.1");
if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&group, sizeof(group)) < 0) {
perror("adding multicast group");
close(sd);
exit(1);
}

/*
* Read from the socket.
*/
datalen = sizeof(databuf);
if (read(sd, databuf, datalen) < 0) {
perror("reading datagram message");
close(sd);
exit(1);
}

128 Socket programming


Example: Establishing SSL server and client communications
The following examples enable a Secure Sockets Layer (SSL) server to
communicate with an SSL client. The steps that are needed for an SSL server differ
from the steps that are needed for an SSL client.

Example: Enabling the SSL server to communicate with the SSL client

The following example enables an SSL server to perform the steps listed below and
to communicate with an SSL client:
1. The server calls SSL_Init() to set up the application certificate if it is needed.
An SSL_Init() call must succeed at least once in a job.
2. The server calls socket() to obtain a socket descriptor.
3. The server calls bind(), listen(), and accept() to activate a connection for a
server program.
4. The server calls SSL_Create() to enable SSL support for the connected socket.
5. The server calls SSL_Handshake() to initiate the SSL handshake negotiation of
the cryptographic parameters.
6. The server calls SSL_Read() and SSL_Write() to receive and send data.
7. The server calls SSL_Destroy() to disable SSL support for the socket.
8. The server calls close() to destroy the connected sockets.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ssl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

/* bufferLen is 250 bytes */


#define bufferLen 250

void main()
{
int bufferLen, on = 1, rc = 0, sd, sd2, addrlen = 0;
char buffer[bufferLen];

SSLInit sslinit;
SSLHandle* sslh;

struct sockaddr_in addr;

unsigned short int cipher[3] = {


SSL_RSA_WITH_RC4_128_MD5,
SSL_RSA_WITH_RC4_128_SHA,
SSL_RSA_EXPORT_WITH_RC4_40_MD5
};

/*************************************************/
/* memset sslinit structure to hex zeros and */
/* fill in values for the sslinit structure */
/*************************************************/
memset((char *)&SSL_Init, 0x00, sizeof(sslinit));
sslinit.keyringFileName = "/keyringfile.kyr";
sslinit.keyringPassword = NULL;
sslinit.cipherSuiteList = &cipher[0];
sslinit.cipherSuiteListLen = 3;

/*************************************************/
/* Initialize SSL security call SSL_Init */

Chapter 7. Examples: Using sockets 129


/*************************************************/
rc = SSL_Init(&sslinit);
if (rc != 0)
{
printf("SSL_Init() failed with rc = %d "
"and errno = %d.\n",rc,errno);
return;
}

/*************************************************/
/* Initialize a socket */
/*************************************************/
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
return;
}

/*************************************************/
/* Allow the socket descriptor to be reusable */
/*************************************************/
rc = setsockopt(sd, SOL_SOCKET,
SO_REUSEADDR,
(char *)&on,
sizeof(on));
if (rc < 0)
{
perror("setsockopt() failed");
return;
}

/*************************************************/
/* memset the sockaddr structure to hex zeros */
/* and bind the local server address */
/*************************************************/
memset((char *) &addr, 0x00, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 12345;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
rc = bind(sd, (struct sockaddr *) &addr,
sizeof(addr));
if (rc < 0)
{
perror("bind() failed");
close(sd);
return;
}

/*************************************************/
/* Enable client connections */
/*************************************************/
listen(sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(sd);
return;
}

/*************************************************/
/* Accept a client connection */
/*************************************************/
addrlen = sizeof(addr);
sd2 = accept(sd, (struct sockaddr *) &addr,
&addrlen);
if (sd2 < 0)

130 Socket programming


{
perror("accept() failed");
close(sd);
return;
}

/*************************************************/
/* Enable SSL support for the connection */
/*************************************************/
sslh = SSL_Create(sd, SSL_ENCRYPT);
if (sslh == NULL)
{
printf("SSL_Create() failed with errno = %d.\n",
errno);
close(sd);
close(sd2);
return;
}

/*************************************************/
/* Set the input fields of the SSLHandle */
/* structure */
/*************************************************/
sslh -> protocol = 3;
sslh -> timeout = 0;
sslh -> cipherSuiteList = &cipher[0]
sslh -> cipherSuiteListLen = 3;

/*************************************************/
/* Initiate the SSL handshake */
/*************************************************/
rc = SSL_Handshake(sslh,
SSL_HANDSHAKE_AS_SERVER_WITH_CLIENT_AUTH);
if (rc != 0)
{
printf("SSL_Handshake() failed with rc = %d "
"and errno = %d.\n", rc, errno);
close(sd);
close(sd2);
return;
}

/*************************************************/
/* memset buffer to hex zeros */
/*************************************************/
memset((char *) buffer, 0x00, bufferLen);

/*************************************************/
/* Receive a message from the client using the */
/* secure session */
/*************************************************/
rc = SSL_Read(sslh, buffer, bufferLen);
if (rc < 0)
{
printf("SSL_Read() failed with rc = %d.\n",rc);
SSL_Destroy(sslh);
close(sd);
close(sd2);
return;
}

/*************************************************/
/* Send the message to the client using the */
/* secure session */
/*************************************************/
bufferLen = strlen(buffer);
rc = SSL_Write(sslh, buffer, bufferLen);

Chapter 7. Examples: Using sockets 131


if (rc != bufferLen)
{
if (rc < 0)
{
printf("SSL_Write() failed with rc = %d.\n",rc);
SSL_Destroy(sslh);
close(sd);
close(sd2);
return;
}
else
{
printf("SSL_Write() did not write all data.\n");
SSL_Destroy(sslh);
close(sd);
close(sd2);
return;
}
}

/*************************************************/
/* Disable SSL support for the connection */
/*************************************************/
SSL_Destroy(sslh);

/*************************************************/
/* Close the connection */
/*************************************************/
close(sd2);

/*************************************************/
/* Close the listener */
/*************************************************/
close(sd);

return;
}

Example: Enabling the SSL client to communicate with the server

The following example enables an SSL client to perform the steps listed below and
to communicate with an SSL server:
1. The client calls SSL_Init() to set up the application certificate if the certificate
does not exist. An SSL_Init() call must succeed at least once in a job.
2. The client calls socket() to obtain a socket descriptor.
3. The client calls connect() to activate a connection for a client program.
4. The client calls SSL_Create() to enable SSL support for the connected socket.
5. The client calls SSL_Handshake() to initiate the SSL handshake negotiation of
the cryptographic parameters.
6. The client calls SSL_Read() and SSL_Write() to receive and send data.
7. The client calls SSL_Destroy() to disable SSL support for the socket.
8. The client calls close() to destroy the connected sockets.
#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/socket.h>
#include <ssl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>

132 Socket programming


/* bufferLen is 250 bytes */
#define bufferLen 250

/***************************************************/
/* This program can be called by passing a host */
/* name or passing an IP address or with no */
/* parameters. */
/***************************************************/
void main(int argc, char * argv[])
{
int rc = 0, sd;
char writeBuff[bufferLen];
char readBuff[bufferLen];

SSLHandle *sslh;
SSLInit sslinit;

struct sockaddr_in addr;

struct hostent *host_ent;

unsigned short int cipher[2] = {


SSL_RSA_WITH_NULL_MD5,
SSL_RSA_EXPORT_WITH_RC4_40_MD5
};

/*************************************************/
/* memset sslinit structure to hex zeros and */
/* fill in values for the sslinit structure */
/*************************************************/
memset((char *)&sslinit, 0x00, sizeof(sslinit));
sslinit.keyringFileName = "/keyringfile.kyr";
sslinit.keyringPassword = "secret";
sslinit.cipherSuiteList = &cipher[0];
sslinit.cipherSuiteListLen =
sizeof(cipher) / sizeof(short);

/*************************************************/
/*Initialize SSL security call SSL_Init */
/*************************************************/
rc = SSL_Init(&sslinit);
if (rc != 0) /* did SSL_Init fail? */
{
printf("SSL_Init() failed with rc = %d"
" and errno = %d.\n",rc,errno);
return;
}

/*************************************************/
/* Initialize a socket */
/*************************************************/
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
return;
}

/*************************************************/
/* Determine the host name and IP address of the */
/* machine the server is running on */
/*************************************************/
if (argc < 2)
{
/***********************************************/
/* Use INADDR_ANY if no parameters passed */

Chapter 7. Examples: Using sockets 133


/***********************************************/
addr.sin_addr.s_addr = htonl(INADDR_ANY);
}
else if (isdigit(*argv[1]))
{
/***********************************************/
/* Use the IP address that is passed */
/***********************************************/
addr.sin_addr.s_addr = inet_addr(argv[1]);
}
else
{
/***********************************************/
/* Get the IP address of the host name that */
/* is passed in */
/***********************************************/
host_ent = gethostbyname(argv[1]);
if (host_ent == NULL)
{
printf("Host not found!\n");
return;
}
memcpy((char *)&addr.sin_addr.s_addr,
host_ent->h_addr_list[0],
host_ent->h_length);
}

/*************************************************/
/* Connect to the server */
/*************************************************/
memset((char *) &addr, 0x00, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 12345;
rc = connect(sd, (struct sockaddr *) &addr,
sizeof(addr));
if (rc < 0)
{
perror("connect() failed");
close(sd);
return;
}

/*************************************************/
/* Enable SSL support for the connection */
/*************************************************/
sslh = SSL_Create(sd, SSL_ENCRYPT);
if (sslh == NULL)
{
printf("SSL_Create() failed with errno = %d.\n",
errno);
close(sd);
return;
}

/*************************************************/
/* Set the input fields of the SSLHandle */
/* structure */
/*************************************************/
sslh -> protocol = 3;
sslh -> timeout = 0;
sslh -> cipherSuiteList = NULL;
sslh -> cipherSuiteListLen = 0;

/*************************************************/
/* Initiate the SSL handshake */
/*************************************************/
rc = SSL_Handshake(sslh, SSL_HANDSHAKE_AS_CLIENT);

134 Socket programming


if (rc != 0)
{
printf("SSL_Handshake() failed with rc = %d "
"and errno = %d.\n", rc, errno);
close(sd);
return;
}

/*************************************************/
/* Send a message to the server using the secure */
/* session */
/*************************************************/
strcpy(writeBuff,"Test of SSL_Write \n\n");
bufferLen = strlen(writeBuff);
rc = SSL_Write(sslh, writeBuff, bufferLen);
if (rc != bufferLen)
{
if (rc < 0)
{
printf("SSL_Write() failed with rc = %d "
"and errno = %d.\n",rc, errno);
SSL_Destroy(sslh);
close(sd);
return;
}
else
{
printf("SSL_Write() did not write all data.\n");
SSL_Destroy(sslh);
close(sd);
return;
}
}

/*************************************************/
/* memset readBuff to hex zeros */
/*************************************************/
memset((char *) readBuff, 0x00, bufferLen);

/*************************************************/
/* Receive the message from the server using the */
/* secure session */
/*************************************************/
rc = SSL_Read(sslh, readBuff, bufferLen);
if (rc < 0)
{
printf("SSL_Read() failed with rc = %d.\n",rc);
SSL_Destroy(sslh);
close(sd);
return;
}

/*************************************************/
/* Disable SSL support for the connection */
/*************************************************/
SSL_Destroy(sslh);

/*************************************************/
/* Close the connection */
/*************************************************/
close(sd);

return;
}

Chapter 7. Examples: Using sockets 135


Examples: Establishing communications using the send_file() and
accept_and_recv() APIs
The following examples enable a server to communicate with a client by using the
send_file() and accept_and_recv() APIs. The steps that are needed for the server
example differ from the steps that are needed for the client example.

Example: Connecting a server using the send_file() API

The following example enables a server to perform the steps listed below to
communicate with a client by using the send_file() and accept_and_recv() APIs:
1. The server calls socket(), bind(), and listen() to create a listening socket.
2. The server initializes the local and remote address structures.
3. The server calls accept_and_recv() to wait for an incoming connection and to
wait for the first data buffer to arrive over this connection. This call returns the
number of bytes that is received and the local and remote addresses that are
associated with this connection. This call is a combination of the accept(),
getsockname(), and recv() APIs.
4. The server calls open() to open the file whose name was obtained as data on
the accept_and_recv() from the client application.
5. memset() is used to set all of the fields of the sf_parms structure to an initial
value of 0. The server sets the file descriptor field to the value that open()
returned. The server then sets the file bytes field to -1 to indicate that the server
should send the entire file. The system is not sending a header buffer and
trailer buffer, so these fields do not fill. The system is sending the entire file, so
you do not need to assign the file offset field.
6. The server calls send_file() to transmit the contents of the file. send_file() does
not complete until the entire file has been sent or an interrupt occurs.
send_file() is more efficient because the application does not have to go into a
read() and send() loop until the file finishes.
7. The server specifies the SF_CLOSE flag on the send_file() API. The SF_CLOSE flag
informs the send_file() API that it should automatically close the socket
connection when the last byte of the file and the trailer buffer (if specified)
have been sent successfully. The application does not need to call close() if the
SF_CLOSE flag is specified.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SERVER_PORT 12345

main (int argc, char *argv[])


{
int i, num, rc, flag = 1;
int fd, listen_sd, accept_sd = -1;

size_t local_addr_length;
size_t remote_addr_length;
size_t total_sent;

struct sockaddr_in addr;


struct sockaddr_in local_addr;
struct sockaddr_in remote_addr;
struct sf_parms parms;

136 Socket programming


char buffer[255];

/*************************************************/
/* If an argument is specified, use it to */
/* control the number of incoming connections */
/*************************************************/
if (argc >= 2)
num = atoi(argv[1]);
else
num = 1;

/*************************************************/
/* Create an AF_INET stream socket to receive */
/* incoming connections on */
/*************************************************/
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}

/*************************************************/
/* Set the SO_REUSEADDR bit so that you do not */
/* have to wait 2 minutes before restarting */
/* the server */
/*************************************************/
rc = setsockopt(listen_sd,
SOL_SOCKET,
SO_REUSEADDR,
(char *)&flag,
sizeof(flag));
if (rc < 0)
{
perror("setsockop() failed");
close(listen_sd);
exit(-1);
}

/*************************************************/
/* Bind the socket */
/*************************************************/
memset(&address, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);
rc = bind(listen_sd,
(struct sockaddr *)&addr, sizeof(addr));
if (rc < 0)
{
perror("bind() failed");
close(listen_sd);
exit(-1);
}

/*************************************************/
/* Set the listen backlog */
/*************************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}

Chapter 7. Examples: Using sockets 137


/*************************************************/
/* Initialize the local and remote addr lengths */
/*************************************************/
local_addr_length = sizeof(local_addr);
remote_addr_length = sizeof(remote_addr);

/*************************************************/
/* Inform the user that the server is ready */
/*************************************************/
printf("The server is ready\n");

/*************************************************/
/* Go through the loop once for each connection */
/*************************************************/
for (i=0; i < num; i++)
{
/**********************************************/
/* Wait for an incoming connection */
/**********************************************/
printf("Iteration: %d\n", i+1);
printf(" waiting on accept_and_recv()\n");

rc = accept_and_recv(listen_sd,
&accept_sd,
(struct sockaddr *)&remote_addr,
&remote_addr_length,
(struct sockaddr *)&local_addr,
&local_addr_length,
&buffer,
sizeof(buffer));
if (rc < 0)
{
perror("accept_and_recv() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}
printf(" Request for file: %s\n", buffer);

/**********************************************/
/* Open the file to retrieve */
/**********************************************/
fd = open(buffer, O_RDONLY);
if (fd < 0)
{
perror("open() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}

/**********************************************/
/* Initialize the sf_parms structure */
/**********************************************/
memset(&parms, 0, sizeof(parms));
parms.file_descriptor = fd;
parms.file_bytes = -1;

/**********************************************/
/* Initialize the counter of the total number */
/* of bytes sent */
/**********************************************/
total_sent = 0;

/**********************************************/
/* Loop until the entire file has been sent */
/**********************************************/

138 Socket programming


do
{
rc = send_file(&accept_sd, &parms, SF_CLOSE);
if (rc < 0)
{
perror("send_file() failed");
close(fd);
close(listen_sd);
close(accept_sd);
exit(-1);
}
total_sent += parms.bytes_sent;

} while (rc == 1);

printf(" Total number of bytes sent: %d\n", total_sent);

/**********************************************/
/* Close the file that is sent out */
/**********************************************/
close(fd);
}

/*************************************************/
/* Close the listen socket */
/*************************************************/
close(listen_sd);

/*************************************************/
/* Close the accept socket */
/*************************************************/
if (accept_sd != -1)
close(accept_sd);
}

Example: Connecting a client using the accept_and_recv() API

The following example enables a client to perform the steps listed below and to
communicate with a server by using the accept_and_recv() API:
1. This client program takes from zero to two parameters.
The first parameter (if specified) is the dotted-decimal IP address or the host
name where the server application is located.
The second parameter (if specified) is the name of the file that the client
attempts to obtain from the server. A server application sends the contents of
the specified file to the client. If the user does not specify any parameters, then
the client uses INADDR_ANY for the server’s IP address. If the user does not
specify a second parameter, the program prompts the user to enter a file name.
2. The client calls socket() to create a socket descriptor.
3. The client calls connect() to establish a connection to the server. Step one
obtained the IP address of the server.
4. The client calls send() to inform the server what file name it wants to obtain.
Step one obtained the name of the file.
5. The client goes into a ″do″ loop calling recv() until the end of the file is
reached. A return code of 0 on the recv() means that the server closed the
connection.
6. The client calls close() to close the socket.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>

Chapter 7. Examples: Using sockets 139


#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_PORT 12345

main (int argc, char *argv[])


{
int rc, sockfd;

char filename[256];
char buffer[32 * 1024];

struct sockaddr_in addr;


struct hostent *host_ent;

/*************************************************/
/* Initialize the socket address structure */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);

/*************************************************/
/* Determine the host name and IP address of the */
/* machine the server is running on */
/*************************************************/
if (argc < 2)
{
addr.sin_addr.s_addr = htonl(INADDR_ANY);
}
else if (isdigit(*argv[1]))
{
addr.sin_addr.s_addr = inet_addr(argv[1]);
}
else
{
host_ent = gethostbyname(argv[1]);
if (host_ent == NULL)
{
printf("Host not found!\n");
exit(-1);
}
memcpy((char *)&addr.sin_addr.s_addr,
host_ent->h_addr_list[0],
host_ent->h_length);
}

/**************************************************/
/* Check to see if the user specified a file name */
/* on the command line */
/**************************************************/
if (argc == 3)
{
strcpy(filename, argv[2]);
}
else
{
printf("Enter the name of the file:\n");
gets(filename);
}

/*************************************************/
/* Create an AF_INET stream socket */
/*************************************************/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)

140 Socket programming


{
perror("socket() failed");
exit(-1);
}
printf("Socket completed.\n");

/*************************************************/
/* Connect to the server */
/*************************************************/
rc = connect(sockfd,
(struct sockaddr *)&addr,
sizeof(struct sockaddr_in));
if (rc < 0)
{
perror("connect() failed");
close(sockfd);
exit(-1);
}
printf("Connect completed.\n");

/*************************************************/
/* Send the request over to the server */
/*************************************************/
rc = send(sockfd, filename, strlen(filename) + 1, 0);
if (rc < 0)
{
perror("send() failed");
close(sockfd);
exit(-1);
}
printf("Request for %s sent\n", filename);

/*************************************************/
/* Receive the file from the server */
/*************************************************/
do
{
rc = recv(sockfd, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("recv() failed");
close(sockfd);
exit(-1);
}
else if (rc == 0)
{
printf("End of file\n");
break;
}
printf("%d bytes received\n", rc);

} while (rc > 0);

/*************************************************/
/* Close the socket */
/*************************************************/
close(sockfd);
}

Chapter 7. Examples: Using sockets 141


142 Socket programming


Printed in U.S.A.

Das könnte Ihnen auch gefallen