Beruflich Dokumente
Kultur Dokumente
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
As of V4R3, socket system functions and the socket network functions are
threadsafe.
http://www.adobe.com/prodindex/acrobat/readstep.html .
Socket characteristics:
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
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.
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.
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.
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
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.
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.
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.
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.
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.
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).
sockaddr structure:
struct sockaddr {
u_short sa_family;
char sa_data[14];
};
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
The use of these address families, their related protocols, and examples of relevant
structures follow.
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.
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.
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.
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.
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 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.
Stream (SOCK_STREAM):
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.
In the AS/400 implementation, you can use datagram sockets over user datagram
protocol (UDP), SNA, and IPX networks.
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.
You need the following communications objects for AF_INET sockets over SNA.
For information on communications objects, see the IBM AnyNet Guide to APPC
See IP over IPX for more information about IP over IPX configuration.
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.
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.
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.
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.
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.
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.
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
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
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
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
Bottom
Parameters or command
===> ___________________________________________________________________________________
F3=Exit F4=Prompt F12=Cancel F23=More options F24=More keys
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.
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.
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
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.
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.
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.
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.
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.
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.
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 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
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
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.
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
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.
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)
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.
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
An example of how you can use the sendmsg() API to pass a descriptor to a job
that does not exist follows.
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.
The send_file() verb enables the sending of file data directly from a file system
over a connected socket with a single API call.
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.
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.
The use of the AF_INET sockets depends on the following AS/400 database files:
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
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
56 Socket programming
v sendmsg()
v sendto()
v write()
v writev()
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.
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.
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
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.
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.
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.
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.
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.
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.
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
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??')
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.
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.
with RPG IV? A SorcererÆs Guide to System Access and More redbook.
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 */
/******************************************************************/
/******************************************************************/
/* All done, close and return */
/******************************************************************/
close(xSock);
return(0);
}
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 */
/*****************************************************************/
/* All done, close both sockets and return */
/*****************************************************************/
close(xSock);
close(xNewSock);
return(0);
}
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.
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.
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.
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.
76 Socket programming
ADDIPSLOC RMTDEST('123.4.5.200') SUBNETMASK(*HOST)
RMTNETID(PALLET1) LOCTPL(PURPLE1)
CHGNETA ALWANYNET(*YES)
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
The following figure shows the IP addresses that are given out to the SNA
network.
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)
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.
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.
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.
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')
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()
/* 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
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;
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. */
/*************************************************/
/*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);
}
/***********************************************/
/* 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");
/*****************************************/
/* 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>
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);
/***********************************************/
/* 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. */
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);
}
90 Socket programming
#include <netinet/in.h>
/******************************************/
/* 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");
/*******************************************/
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);
}
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>
/****************************************/
/* 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);
/****************************************/
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);
}
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.
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>
void main()
{
/***************************************/
/* Variable and structure definitions. */
/***************************************/
int sd, rc;
/******************************************/
/* 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);
}
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() */
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>
/******************************************/
/* 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);
/**********************************************/
/* 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);
}
/********************************************/
/* When the data has been received, close() */
/* the socket descriptor. */
/********************************************/
/* close() the socket descriptor. */
close(sd);
exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
/*************************************************/
/* If an argument was specified, use it to */
/*************************************************/
/* 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);
}
/**********************************************/
/* 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);
}
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.
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>
/*************************************************/
/* 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)
/**********************************************/
/* 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);
}
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>
/*************************************************/
/* The descriptor for the incoming connection is */
/*************************************************/
/* 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);
}
This examples show how to design the givedescriptor() server API to handle
incoming connections and to create a pool of worker jobs.
Note:
#define ID_LEN 16
#define SERVER_PORT 12345
/*************************************************/
/* 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);
/*************************************************/
/* Create and clear a data queue */
/*************************************************/
system("CRTDTAQ DTAQ(QUSRSYS/DQ) MAXLEN(20)");
/*************************************************/
/* 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 */
/**********************************************/
/* 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);
}
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
/*************************************************/
/* 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");
/*************************************************/
/* Close the descriptors */
/*************************************************/
close(sockfd);
}
The following figure illustrates how the server, worker, and client jobs interact
when the system uses the sendmsg() and recvmsg() server design.
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>
/*************************************************/
/* 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");
/*************************************************/
/* 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++)
{
/**********************************************/
/* 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);
}
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>
/*************************************************/
/* 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);
}
/*************************************************/
/* 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);
}
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>
/*************************************************/
/* If an argument is specified, use it to */
/* control the number of incoming connections */
/*************************************************/
if (argc >= 2)
num = atoi(argv[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)
{
/*************************************************/
/* Inform the user that the server is ready */
/*************************************************/
printf("The server is ready\n");
/*************************************************/
/* Close the listening socket */
/*************************************************/
close(listen_sd);
}
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>
/*************************************************/
/* 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);
/*************************************************/
/*************************************************/
/* Close the descriptors */
/*************************************************/
close(listen_sd);
close(accept_sd);
}
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>
/*************************************************/
/* 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,
/*************************************************/
/* 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);
}
/********************************************************/
/* 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
/**********************************************************/
/* 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,
}
exit(0);
}
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.
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.
/* ------------------------------------------------------------*/
/* */
/* 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;
/*
* Set local interface for outbound multicast datagrams.
* The IP address specified must be associated with a local,
* multicast-capable interface.
*/
/*
* 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");
}
}
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>
/* ------------------------------------------------------------*/
/* */
/* Receive Multicast Datagram code example. */
/* */
/* ------------------------------------------------------------*/
/*
* Enable SO_REUSEADDR to allow multiple instances of this
* application to receive copies of the multicast datagrams.
*/
{
int reuse=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;
/*
* 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);
}
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>
void main()
{
int bufferLen, on = 1, rc = 0, sd, sd2, addrlen = 0;
char buffer[bufferLen];
SSLInit sslinit;
SSLHandle* sslh;
/*************************************************/
/* 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 */
/*************************************************/
/* 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)
/*************************************************/
/* 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);
/*************************************************/
/* Disable SSL support for the connection */
/*************************************************/
SSL_Destroy(sslh);
/*************************************************/
/* Close the connection */
/*************************************************/
close(sd2);
/*************************************************/
/* Close the listener */
/*************************************************/
close(sd);
return;
}
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>
/***************************************************/
/* 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;
/*************************************************/
/* 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 */
/*************************************************/
/* 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);
/*************************************************/
/* 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;
}
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>
size_t local_addr_length;
size_t remote_addr_length;
size_t total_sent;
/*************************************************/
/* 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);
}
/*************************************************/
/* 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 */
/**********************************************/
/**********************************************/
/* 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);
}
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>
char filename[256];
char buffer[32 * 1024];
/*************************************************/
/* 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)
/*************************************************/
/* 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);
/*************************************************/
/* Close the socket */
/*************************************************/
close(sockfd);
}
Printed in U.S.A.