Rzab 6 PDF
Rzab 6 PDF
Version 7.2
Programming
Socket programming
IBM
Note
Before using this information and the product it supports, read the information in “Notices” on page
179.
This edition applies to version IBM i 7.2 (product number 5770-SS1) and to all subsequent releases and modifications
until otherwise indicated in new editions. This version does not run on all reduced instruction set computer (RISC)
models nor does it run on CISC models.
This document may contain references to Licensed Internal Code. Licensed Internal Code is Machine Code and is
licensed to you under the terms of the IBM License Agreement for Machine Code.
© Copyright International Business Machines Corporation 2001, 2013.
US Government Users Restricted Rights – Use, duplication or disclosure restricted by GSA ADP Schedule Contract with
IBM Corp.
Contents
Socket programming............................................................................................. 1
What's new for IBM i 7.2..............................................................................................................................1
PDF files for Socket programming............................................................................................................... 1
Prerequisites for socket programming........................................................................................................3
How sockets work........................................................................................................................................ 3
Socket characteristics..................................................................................................................................6
Socket address structure ...................................................................................................................... 7
Socket address family............................................................................................................................ 8
Socket type........................................................................................................................................... 12
Socket protocols...................................................................................................................................13
Basic socket design................................................................................................................................... 14
Creating a connection-oriented socket............................................................................................... 14
Creating a connectionless socket........................................................................................................ 21
Designing applications with address families..................................................................................... 26
Advanced socket concepts........................................................................................................................39
Asynchronous I/O.................................................................................................................................39
Secure sockets..................................................................................................................................... 43
Client SOCKS support...........................................................................................................................51
Thread safety........................................................................................................................................54
Nonblocking I/O................................................................................................................................... 54
Signals.................................................................................................................................................. 56
IP multicasting..................................................................................................................................... 57
File data transfer—send_file() and accept_and_recv()..........................................................58
Out-of-band data..................................................................................................................................58
I/O multiplexing—select().................................................................................................................... 60
Socket network functions.................................................................................................................... 60
Domain Name System support............................................................................................................ 61
Berkeley Software Distribution compatibility..................................................................................... 64
UNIX 98 compatibility..........................................................................................................................66
Descriptor passing between processes: sendmsg() and recvmsg()................................................... 70
Sockets-related User Exit Points......................................................................................................... 72
Socket scenario: Creating an application to accept IPv4 and IPv6 clients..............................................74
Example: Accepting connections from both IPv6 and IPv4 clients................................................... 75
Example: IPv4 or IPv6 client ...............................................................................................................80
Socket application design recommendations...........................................................................................82
Examples: Socket application designs......................................................................................................86
Examples: Connection-oriented designs.............................................................................................86
Example: Using asynchronous I/O.................................................................................................... 108
Examples: Establishing secure connections..................................................................................... 114
Example: Using gethostbyaddr_r() for threadsafe network routines....................................... 138
Example: Nonblocking I/O and select()....................................................................................... 140
Using poll() instead of select()................................................................................................. 145
Example: Using signals with blocking socket APIs........................................................................... 150
Examples: Using multicasting with AF_INET.................................................................................... 153
Example: Updating and querying DNS...............................................................................................158
Examples: Transferring file data using send_file() and accept_and_recv() APIs...............161
Xsockets tool........................................................................................................................................... 167
Configuring Xsockets......................................................................................................................... 167
Configuring Xsockets to use a Web browser..................................................................................... 170
Using Xsockets .................................................................................................................................. 174
Deleting objects created by the Xsockets tool..................................................................................176
iii
Customizing Xsockets........................................................................................................................ 176
Serviceability tools.................................................................................................................................. 176
Notices..............................................................................................................179
Programming interface information........................................................................................................180
Trademarks..............................................................................................................................................180
Terms and conditions.............................................................................................................................. 181
iv
Socket programming
A socket is a communications connection point (endpoint) that you can name and address in a network.
Socket programming shows how to use socket APIs to establish communication links between remote
and local processes.
The processes that use a socket can reside on the same system or different systems on different
networks. Sockets are useful for both stand-alone and network applications. Sockets allow you to
exchange information between processes on the same machine or across a network, distribute work to
the most efficient machine, and they easily allow access to centralized data. Socket application program
interfaces (APIs) are the network standard for TCP/IP. A wide range of operating systems support socket
APIs. IBM® i sockets support multiple transport and networking protocols. Socket system functions and
the socket network functions are threadsafe.
Programmers who use Integrated Language Environment® (ILE) C can refer to this topic collection to
develop socket applications. You can also code to the sockets API from other ILE languages, such as RPG.
The Java™ language also supports a socket programming interface.
Note: By using the examples, you agree to the terms of the “Code license and disclaimer information” on
page 177.
Other information
You can also view or print any of the following PDFs:
IBM Redbooks®:
• Who Knew You Could Do That with RPG IV? A Sorcerer's Guide to System Access and More (5630
KB)
– RFC 5246: "The Transport Layer Security (TLS) Protocol Version 1.2"
– RFC 4346: "The Transport Layer Security (TLS) Protocol Version 1.1"
– RFC 2246: "The TLS Protocol Version 1.0 "
– RFC 2560: "X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP"
• Other Web Resources
Compiler requirements
1. Install QSYSINC library. This library provides necessary header files that are needed when compiling
socket applications.
2. Install the ILE C licensed program (5770-WDS Option 51).
Requirements for Secure Sockets Layer (SSL) APIs and Global Security Kit (GSKit) APIs
In addition to the requirements for compiler, AF_INET address families, and AF_INET6 address families,
you must complete the following tasks to work with secure sockets:
1. Install and configure Digital Certificate Manager licensed program (5770-SS1 Option 34). See Digital
Certificate Manager (DCM) in the information center for details.
2. If you want to use SSL with the cryptographic hardware, you need to install and configure the 4765
Cryptographic Coprocessor. See Cryptography for complete descriptions of the 4765 Cryptographic
Coprocessor.
Related reference
Using AF_INET address family
AF_INET address family sockets can be either connection-oriented (type SOCK_STREAM) or
connectionless (type SOCK_DGRAM). Connection-oriented AF_INET sockets use Transmission Control
Protocol (TCP) as the transport protocol. Connectionless AF_INET sockets use User Datagram Protocol
(UDP) as the transport protocol.
Using AF_INET6 address family
AF_INET6 sockets provide support for Internet Protocol version 6 (IPv6) 128 bit (16 byte) address
structures. Programmers can write applications using the AF_INET6 address family to accept client
requests from either IPv4 or IPv6 nodes, or from IPv6 nodes only.
Socket programming 3
structures compatible with BSD 4.4 and the UNIX 98 programming interface specifications. Programmers
can specify _XOPEN_SOURCE macro to use the UNIX 98 compatible interface.
The following figure shows the typical flow of events (and the sequence of issued APIs) for a connection-
oriented socket session. An explanation of each event follows the figure.
Typically, a network configuration does not allow connections between a secure internal network and a
less secure external network. However, you can enable sockets to communicate with server programs
that run on a system outside a firewall (a very secure host).
Sockets are also a part of IBM's AnyNet® implementation for the Multiprotocol Transport Networking
(MPTN) architecture. MPTN architecture provides the ability to operate a transport network over
additional transport networks and to connect application programs across transport networks of different
types.
Related reference
Berkeley Software Distribution compatibility
Sockets is a Berkeley Software Distribution (BSD) interface.
UNIX 98 compatibility
Created by The Open Group, a consortium of developers and venders, UNIX 98 improved the inter-
operability of the UNIX operating system while incorporating much of the Internet-related function for
which UNIX had become known.
Related information
socket()--Create Socket API
listen()--Invite Incoming Connections Requests API
bind()--Set Local Address for Socket API
accept()--Wait for Connection Request and Make Connection API
send()--Send Data API
recv()--Receive Data API
close()--Close File or Socket Descriptor API
Sockets APIs
API finder
Socket programming 5
Socket characteristics
Sockets share some common characteristics.
• A socket is represented by an integer. That integer is called a socket descriptor.
• A socket exists as long as the process maintains an open link to the socket.
• You can name a socket and use it to communicate with other sockets in a communication domain.
• Sockets perform the communication when the server accepts connections from them, or when it
exchanges messages with them.
• You can create sockets in pairs (only for sockets in the AF_UNIX address family).
The connection that a socket provides can be connection-oriented or connectionless. Connection-oriented
communication implies that a connection is established, and a dialog between the programs follows. The
program that provides the service (the server program) establishes the available socket that is enabled to
accept incoming connection requests. Optionally, the server can assign a name to the service that it
supplies, which allows clients to identify where to obtain and how to connect to that service. The client of
the service (the client program) must request the service of the server program. The client does this by
connecting to the distinct name or to the attributes associated with the distinct name that the server
program has designated. It is similar to dialing a telephone number (an identifier) and making a
connection with another party that is offering a service (for example, a plumber). When the receiver of the
call (the server, in this example, a plumber) answers the telephone, the connection is established. The
plumber verifies that you have reached the correct party, and the connection remains active as long as
both parties require it.
Connectionless communication implies that no connection is established, over which a dialog or data
transfer can take place. Instead, the server program designates a name that identifies where to reach it
(much like a post-office box). If you send a letter to a post office box, you cannot be absolutely sure that
the receiver got the letter. You might need to wait for a response to your letter. There is no active, real-
time connection, in which data is exchanged.
In addition to these socket characteristics or parameters, constant values are defined in network routines
and header files that are shipped with the QSYSINC library. For descriptions of header files, see the
individual APIs. Each API lists its appropriate header file in the usage section of the API description.
Socket network routines allow socket applications to obtain information from the Domain Name System
(DNS), host, protocol, service, and network files.
Related reference
Socket network functions
Socket network functions allow application programs to obtain information from the host, protocol,
service, and network files.
Related information
Sockets APIs
Table 2. Comparison of BSD 4.3 and BSD 4.4/ UNIX 98 socket address structure
BSD 4.3 structure BSD 4.4/ UNIX 98 compatible structure
struct sockaddr_storage{
sa_family_t ss_family; struct sockaddr_storage {
char _ss_pad1[_SS_PAD1SIZE]; uint8_t ss_len;
char* _ss_align; sa_family_t ss_family;
char _ss_pad2[_SS_PAD2SIZE]; char _ss_pad1[_SS_PAD1SIZE];
}; char* _ss_align;
char _ss_pad2[_SS_PAD2SIZE];
};
Socket programming 7
Table 3. Address structure
Address structure field Definition
sa_len This field contains the length of the address for
UNIX 98 specifications.
Note: The sa_len field is provided only for BSD 4.4
compatibility. It is not necessary to use this field
even for BSD 4.4/UNIX 98 compatibility. The field
is ignored on input addresses.
Related reference
Using AF_INET address family
AF_INET address family sockets can be either connection-oriented (type SOCK_STREAM) or
connectionless (type SOCK_DGRAM). Connection-oriented AF_INET sockets use Transmission Control
Protocol (TCP) as the transport protocol. Connectionless AF_INET sockets use User Datagram Protocol
(UDP) as the transport protocol.
Socket programming 9
Table 6. Differences between BSD 4.3 and BSD 4.4/ UNIX 98 for sockaddr_in6 address structure
BSD 4.3 sockaddr_in6 address structure BSD 4.4/ UNIX 98 sockaddr_in6 address
structure
For the AF_UNIX address family, protocol specifications do not apply because protocol standards are not
involved. The communications mechanism that the two processes use is specific to the system.
Related reference
Using AF_UNIX address family
Sockets that use the AF_UNIX or AF_UNIX_CCSID address family can be connection-oriented (type
SOCK_STREAM) or connectionless (type SOCK_DGRAM).
AF_UNIX_CCSID address family
The AF_UNIX_CCSID family is compatible with the AF_UNIX address family and has the same limitations.
Related information
unlink()--Remove Link to File API
struct sockaddr_unc {
short sunc_family;
short sunc_format;
char sunc_zero[12];
Qlg_Path_Name_T sunc_qlg;
union {
char unix[126];
Socket programming 11
wchar_t wide[126];
char* p_unix;
wchar_t* p_wide;
} sunc_path;
} ;
Related reference
Using AF_UNIX_CCSID address family
AF_UNIX_CCSID address family sockets have the same specifications as AF_UNIX address family sockets.
AF_UNIX_CCSID address family sockets can be connection-oriented or connectionless. They can provide
communication on the same system.
AF_UNIX address family
This address family provides interprocess communication on the same system that uses the socket APIs.
The address is actually a path name to an entry in the file system.
Related information
Path name format
Socket type
The second parameter on a socket call determines the socket type. Socket type provides the type
identification and characteristics of the connection that are enabled for data transportation from one
machine or process to another.
The system supports the following socket types:
Stream (SOCK_STREAM)
This type of socket is connection-oriented. Establish an end-to-end connection by using the bind(),
listen(), accept(), and connect() APIs. SOCK_STREAM sends data without errors or duplication,
and receives the data in the sending order. SOCK_STREAM builds flow control to avoid data overruns. It
does not impose record boundaries on the data. SOCK_STREAM considers the data to be a stream of
Datagram (SOCK_DGRAM)
In Internet Protocol terminology, the basic unit of data transfer is a datagram. This is basically a header
followed by some data. The datagram socket is connectionless. It establishes no end-to-end connection
with the transport provider (protocol). The socket sends datagrams as independent packets with no
guarantee of delivery. You might lose or duplicate data. Datagrams might arrive out of order. The size of
the datagram is limited to the data size that you can send in a single transaction. For some transport
providers, each datagram can use a different route through the network. You can issue a connect() API
on this type of socket. However, on the connect() API, you must specify the destination address that
the program sends to and receives from. In the IBM i implementation, you can use datagram sockets over
User Datagram Protocol (UDP), AF_UNIX, and AF_UNIX_CCSID.
Raw (SOCK_RAW)
This type of socket allows direct access to lower-layer protocols, such as Internet Protocol (IPv4 or IPv6)
and Internet Control Message Protocol (ICMP or ICMP6). SOCK_RAW requires more programming
expertise because you manage the protocol header information used by the transport provider. At this
level, the transport provider can dictate the format of the data and the semantics that are transport-
provider specific.
Socket protocols
Socket protocols provide the network transportation of application data from one machine to another (or
from one process to another within the same machine).
The application specifies the transport provider on the protocol parameter of the socket() API.
For the AF_INET address family, more than one transport provider is allowed. The protocols of Systems
Network Architecture (SNA) and TCP/IP can be active on the same listening socket at the same time. The
ALWANYNET (Allow ANYNET support) network attribute allows a customer to select whether a transport
other than TCP/IP can be used for AF_INET socket applications. This network attribute can be either *YES
or *NO. The default value is *NO.
For example, if the current status (the default status) is *NO, the use of AF_INET over an SNA transport is
not active. If AF_INET sockets are to be used over a TCP/IP transport only, the ALWANYNET status should
be set to *NO to improve CPU utilization.
Note: The ALWANYNET network attribute also affects APPC over TCP/IP.
The AF_INET and AF_INET6 sockets over TCP/IP can also specify a SOCK_RAW type, which means that
the socket communicates directly with the network layer known as Internet Protocol (IP). The TCP or UDP
transport providers normally communicate with this layer. When you use SOCK_RAW sockets, the
application program specifies any protocol between 0 and 255 (except the TCP and UDP protocols). This
protocol number then flows in the IP headers when machines are communicating on the network. In fact,
the application program is the transport provider, because it must provide for all the transport services
that UDP or TCP transports normally provide.
For the AF_UNIX and AF_UNIX_CCSID address families, a protocol specification is not really meaningful
because there are no protocol standards involved. The communications mechanism between two
processes on the same machine is specific to the machine.
Related information
Configuring APPC, APPN, and HPR
Socket programming 13
Basic socket design
These examples illustrate the most common types of socket programs that use the most basic design,
which can be a basis for more complex socket designs.
Socket programming 15
connect()--Establish Connection or Destination Address API
/**************************************************************************/
/* This sample program provides a code for a connection-oriented server. */
/**************************************************************************/
/**************************************************************************/
/* Header files needed for this sample program . */
/**************************************************************************/
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/poll.h>
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PORT 12345
#define BUFFER_LENGTH 250
#define FALSE 0
void main()
{
/***********************************************************************/
/* Variable and structure definitions. */
/***********************************************************************/
int sd=-1, sd2=-1;
int rc, length, on=1;
char buffer[BUFFER_LENGTH];
struct pollfd fds;
nfds_t nfds = 1;
int timeout;
struct sockaddr_in6 serveraddr;
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of each of the socket descriptors is only done once at the */
/* very end of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor, representing */
/* an endpoint. The statement also identifies that the INET6 */
/* (Internet Protocol version 6) address family with the TCP */
/* transport (SOCK_STREAM) will be used for this socket. */
/********************************************************************/
sd = socket(AF_INET6, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
break;
}
/********************************************************************/
/* The setsockopt() function is used to allow the local address to */
/* be reused when the server is restarted before the required wait */
/* time expires. */
/********************************************************************/
rc = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
if (rc < 0)
{
perror("setsockopt(SO_REUSEADDR) failed");
break;
}
/********************************************************************/
/* 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 will queue 10 incoming connection */
/* requests before the system starts rejecting the incoming */
/* requests. */
/********************************************************************/
rc = listen(sd, 10);
if (rc< 0)
{
perror("listen() failed");
break;
}
/********************************************************************/
/* The server uses the accept() function to accept an incoming */
/* connection request. The accept() call will block indefinitely */
/* waiting for the incoming connection to arrive. */
/********************************************************************/
sd2 = accept(sd, NULL, NULL);
if (sd2 < 0)
{
perror("accept() failed");
break;
}
/********************************************************************/
/* The poll() 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. A 30 second timeout is used on this poll */
/* call. */
/********************************************************************/
timeout = 30000;
memset(&fds, 0, sizeof(fds));
fds.fd = ds2;
fds.events = POLLIN;
fds.revents = 0;
if (rc == 0)
{
printf("poll() timed out.\n");
break;
}
/********************************************************************/
/* In this example we know that the client will send 250 bytes of */
/* data over. Knowing this, we can use the SO_RCVLOWAT socket */
/* option and specify that we don't want our recv() to wake up until*/
/* all 250 bytes of data have arrived. */
/********************************************************************/
length = BUFFER_LENGTH;
rc = setsockopt(sd2, SOL_SOCKET, SO_RCVLOWAT,
(char *)&length, sizeof(length));
Socket programming 17
if (rc < 0)
{
perror("setsockopt(SO_RCVLOWAT) failed");
break;
}
/********************************************************************/
/* Receive that 250 bytes data from the client */
/********************************************************************/
rc = recv(sd2, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("recv() failed");
break;
}
/********************************************************************/
/* Echo the data back to the client */
/********************************************************************/
rc = send(sd2, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("send() failed");
break;
}
/********************************************************************/
/* Program complete */
/********************************************************************/
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
if (sd2 != -1)
close(sd2);
}
Related reference
Using AF_INET address family
AF_INET address family sockets can be either connection-oriented (type SOCK_STREAM) or
connectionless (type SOCK_DGRAM). Connection-oriented AF_INET sockets use Transmission Control
Protocol (TCP) as the transport protocol. Connectionless AF_INET sockets use User Datagram Protocol
(UDP) as the transport protocol.
Example: A connection-oriented client
This example shows how to create a socket client program to connect to a connection-oriented server in a
connection-oriented design.
/**************************************************************************/
/* This sample program provides a code for a connection-oriented client. */
/**************************************************************************/
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PORT 3005
#define BUFFER_LENGTH 250
#define FALSE 0
#define SERVER_NAME "ServerHostName"
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of the socket descriptor is only done once at the very end */
/* of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor, representing */
/* an endpoint. The statement also identifies that the INET6 */
/* (Internet Protocol version 6) address family with the TCP */
/* transport (SOCK_STREAM) will be used for this socket. */
/********************************************************************/
sd = socket(AF_INET6, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
break;
}
/********************************************************************/
/* If an argument was passed in, use this as the server, otherwise */
/* use the #define that is located at the top of this program. */
/********************************************************************/
if (argc > 1)
strcpy(server, argv[1]);
else
strcpy(server, SERVER_NAME);
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin6_family = AF_INET6;
serveraddr.sin6_port = htons(SERVER_PORT);
rc = inet_pton(AF_INET6, server, &serveraddr.sin6_addr.s6_addr);
if (rc != 1)
{
/*****************************************************************/
/* The server string that was passed into the inet_pton() */
/* function was not a hexidecimal colon IP address. It must */
/* therefore be the hostname of the server. Use the */
/* getaddrinfo() function to retrieve the IP address of the */
/* server. */
/*****************************************************************/
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_flags = AI_V4MAPPED;
rc = getaddrinfo(server, NULL, &hints, &res);
if (rc != 0)
Socket programming 19
{
printf("Host not found! (%s)\n", server);
perror("getaddrinfo() failed\n");
break;
}
memcpy(&serveraddr.sin6_addr,
(&((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr),
sizeof(serveraddr.sin6_addr));
freeaddrinfo(res);
}
/********************************************************************/
/* Use the connect() function to establish a connection to the */
/* server. */
/********************************************************************/
rc = connect(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (rc < 0)
{
perror("connect() failed");
break;
}
/********************************************************************/
/* Send 250 bytes of a's to the server */
/********************************************************************/
memset(buffer, 'a', sizeof(buffer));
rc = send(sd, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("send() failed");
break;
}
/********************************************************************/
/* In this example we know that the server is going to respond with */
/* the same 250 bytes that we just sent. Since we know that 250 */
/* bytes are going to be sent back to us, we can use the */
/* SO_RCVLOWAT socket option and then issue a single recv() and */
/* retrieve all of the data. */
/* */
/* The use of SO_RCVLOWAT is already illustrated in the server */
/* side of this example, so we will do something different here. */
/* The 250 bytes of the data may arrive in separate packets, */
/* therefore we will issue recv() over and over again until all */
/* 250 bytes have arrived. */
/********************************************************************/
bytesReceived = 0;
while (bytesReceived < BUFFER_LENGTH)
{
rc = recv(sd, & buffer[bytesReceived],
BUFFER_LENGTH - bytesReceived, 0);
if (rc < 0)
{
perror("recv() failed");
break;
}
else if (rc == 0)
{
printf("The server closed the connection\n");
break;
}
/*****************************************************************/
/* Increment the number of bytes that have been received so far */
/*****************************************************************/
bytesReceived += rc;
}
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
}
Socket programming 21
3. The server uses the recvfrom() API to receive that data. The recvfrom() API waits indefinitely for
data to arrive.
4. The sendto() API echoes the data back to the client.
5. The close() API ends any open socket descriptors.
/**************************************************************************/
/* This sample program provides a code for a connectionless server. */
/**************************************************************************/
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PORT 3555
#define BUFFER_LENGTH 100
#define FALSE 0
void main()
{
/***********************************************************************/
/* Variable and structure definitions. */
/***********************************************************************/
int sd=-1, rc;
char buffer[BUFFER_LENGTH];
struct sockaddr_in6 serveraddr;
struct sockaddr_in6 clientaddr;
int clientaddrlen = sizeof(clientaddr);
/***********************************************************************/
/********************************************************************/
/* After the socket descriptor is created, a bind() function gets a */
/* unique name for the socket. In this example, the user sets the */
/* s_addr to zero, which means that the UDP port of 3555 will be */
/* bound to all IP addresses on the system. */
/********************************************************************/
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin6_family = AF_INET6;
serveraddr.sin6_port = htons(SERVER_PORT);
memcpy(&serveraddr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
/********************************************************************/
/* The server uses the recvfrom() function to receive that data. */
/* The recvfrom() function waits indefinitely for data to arrive. */
/********************************************************************/
rc = recvfrom(sd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&clientaddr,
&clientaddrlen);
if (rc < 0)
{
perror("recvfrom() failed");
break;
}
/********************************************************************/
/* Echo the data back to the client */
/********************************************************************/
rc = sendto(sd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&clientaddr,
sizeof(clientaddr));
if (rc < 0)
{
perror("sendto() failed");
break;
}
/********************************************************************/
/* Program complete */
/********************************************************************/
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
}
Socket programming 23
Example: A connectionless client
This example shows how to use User Datagram Protocol (UDP) to connect a connectionless socket client
program to a server.
Note: By using the examples, you agree to the terms of the “Code license and disclaimer information” on
page 177.
/**************************************************************************/
/* This sample program provides a code for a connectionless client. */
/**************************************************************************/
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PORT 3555
#define BUFFER_LENGTH 100
#define FALSE 0
#define SERVER_NAME "ServerHostName"
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of the socket descriptor is only done once at the very end */
/* of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor, representing */
/* an endpoint. The statement also identifies that the INET6 */
/* (Internet Protocol) address family with the UDP transport */
/* (SOCK_DGRAM) will be used for this socket. */
/********************************************************************/
sd = socket(AF_INET6, SOCK_DGRAM, 0);
if (sd < 0)
{
perror("socket() failed");
break;
}
/********************************************************************/
/* If an argument was passed in, use this as the server, otherwise */
/* use the #define that is located at the top of this program. */
/********************************************************************/
if (argc > 1)
strcpy(server, argv[1]);
else
strcpy(server, SERVER_NAME);
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin6_family = AF_INET6;
serveraddr.sin6_port = htons(SERVER_PORT);
rc = inet_pton(AF_INET6, server, &serveraddr.sin6_addr.s6_addr);
memcpy(&serveraddr.sin6_addr,
(&((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr),
sizeof(serveraddr.sin6_addr));
freeaddrinfo(res);
}
/********************************************************************/
/* Initialize the data block that is going to be sent to the server */
/********************************************************************/
memset(buffer, 0, sizeof(buffer));
strcpy(buffer, "A CLIENT REQUEST");
/********************************************************************/
/* Use the sendto() function to send the data to the server. */
/********************************************************************/
rc = sendto(sd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&serveraddr,
sizeof(serveraddr));
if (rc < 0)
{
perror("sendto() failed");
break;
}
/********************************************************************/
/* Use the recvfrom() function to receive the data back from the */
/* server. */
/********************************************************************/
rc = recvfrom(sd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&serveraddr,
& serveraddrlen);
if (rc < 0)
{
perror("recvfrom() failed");
break;
}
/********************************************************************/
/* Program complete */
/********************************************************************/
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
}
Socket programming 25
Designing applications with address families
These scenarios illustrate how to design applications with each of the socket address families, such as
AF_INET address family, AF_INET6 address family, AF_UNIX address family, and AF_UNIX_CCSID
address family.
::FFFF:192.1.1.1
These addresses can be generated automatically by the getaddrinfo() API, when the specified host
has only IPv4 addresses.
You can create applications that use AF_INET6 sockets to open TCP connections to IPv4 nodes. To
accomplish this task, you can encode the destination's IPv4 address as an IPv4–mapped IPv6 address
and pass that address within a sockaddr_in6 structure in the connect() or sendto() call. When
applications use AF_INET6 sockets to accept TCP connections from IPv4 nodes, or receive UDP packets
Socket programming 27
Socket flow of events: Server application that uses AF_UNIX address family
The first example uses the following sequence of API calls:
1. The socket() API returns a socket descriptor, which represents an endpoint. The statement also
identifies the UNIX address family with the stream transport (SOCK_STREAM) being used for this
socket. You can also use the socketpair() API to initialize a UNIX socket.
AF_UNIX or AF_UNIX_CCSID are the only address families to support the socketpair() API. The
socketpair() API returns two socket descriptors that are unnamed and connected.
2. After the socket descriptor is created, the bind() API gets a unique name for the socket.
The name space for UNIX domain sockets consists of path names. When a sockets program calls the
bind() API, an entry is created 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() API to remove
the directory entry when it ends.
3. The listen() allows the server to accept incoming client connections. In this example, the backlog is
set to 10. This means that the system queues 10 incoming connection requests before the system
starts rejecting the incoming requests.
4. The recv() API receives data from the client application. In this example, the client sends 250 bytes
of data over. Thus, the SO_RCVLOWAT socket option can be used, which specifies that recv() is not
required to wake up until all 250 bytes of data have arrived.
5. The send() API echoes the data back to the client.
6. The close() API closes any open socket descriptors.
7. The unlink() API removes the UNIX path name from the file system.
Socket flow of events: Client application that uses AF_UNIX address family
The second example uses the following sequence of API calls:
/**************************************************************************/
/* This example program provides code for a server application that uses */
/* AF_UNIX address family */
/**************************************************************************/
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
/**************************************************************************/
/* Constants used by this program */
Socket programming 29
/**************************************************************************/
#define SERVER_PATH "/tmp/server"
#define BUFFER_LENGTH 250
#define FALSE 0
void main()
{
/***********************************************************************/
/* Variable and structure definitions. */
/***********************************************************************/
int sd=-1, sd2=-1;
int rc, length;
char buffer[BUFFER_LENGTH];
struct sockaddr_un serveraddr;
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of each of the socket descriptors is only done once at the */
/* very end of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor, which represents */
/* an endpoint. The statement also identifies that the UNIX */
/* address family with the stream transport (SOCK_STREAM) will be */
/* used for this socket. */
/********************************************************************/
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
break;
}
/********************************************************************/
/* After the socket descriptor is created, a bind() function gets a */
/* unique name for the socket. */
/********************************************************************/
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path, SERVER_PATH);
/********************************************************************/
/* 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 will queue 10 incoming connection */
/* requests before the system starts rejecting the incoming */
/* requests. */
/********************************************************************/
rc = listen(sd, 10);
if (rc< 0)
{
perror("listen() failed");
break;
}
/********************************************************************/
/* The server uses the accept() function to accept an incoming */
/* connection request. The accept() call will block indefinitely */
/* waiting for the incoming connection to arrive. */
/********************************************************************/
sd2 = accept(sd, NULL, NULL);
if (sd2 < 0)
{
perror("accept() failed");
break;
}
/********************************************************************/
/* In this example we know that the client will send 250 bytes of */
/* data over. Knowing this, we can use the SO_RCVLOWAT socket */
/* option and specify that we don't want our recv() to wake up */
/********************************************************************/
/* Echo the data back to the client */
/********************************************************************/
rc = send(sd2, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("send() failed");
break;
}
/********************************************************************/
/* Program complete */
/********************************************************************/
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
if (sd2 != -1)
close(sd2);
/***********************************************************************/
/* Remove the UNIX path name from the file system */
/***********************************************************************/
unlink(SERVER_PATH);
}
/**************************************************************************/
/* This sample program provides code for a client application that uses */
/* AF_UNIX address family */
/**************************************************************************/
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
Socket programming 31
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PATH "/tmp/server"
#define BUFFER_LENGTH 250
#define FALSE 0
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of the socket descriptor is only done once at the very end */
/* of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor, which represents */
/* an endpoint. The statement also identifies that the UNIX */
/* address family with the stream transport (SOCK_STREAM) will be */
/* used for this socket. */
/********************************************************************/
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
break;
}
/********************************************************************/
/* If an argument was passed in, use this as the server, otherwise */
/* use the #define that is located at the top of this program. */
/********************************************************************/
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sun_family = AF_UNIX;
if (argc > 1)
strcpy(serveraddr.sun_path, argv[1]);
else
strcpy(serveraddr.sun_path, SERVER_PATH);
/********************************************************************/
/* Use the connect() function to establish a connection to the */
/* server. */
/********************************************************************/
rc = connect(sd, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr));
if (rc < 0)
{
perror("connect() failed");
break;
}
/********************************************************************/
/* Send 250 bytes of a's to the server */
/********************************************************************/
memset(buffer, 'a', sizeof(buffer));
rc = send(sd, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("send() failed");
break;
}
/********************************************************************/
/* In this example we know that the server is going to respond with */
/* the same 250 bytes that we just sent. Since we know that 250 */
/* bytes are going to be sent back to us, we can use the */
/* SO_RCVLOWAT socket option and then issue a single recv() and */
/* retrieve all of the data. */
/* */
/*****************************************************************/
/* Increment the number of bytes that have been received so far */
/*****************************************************************/
bytesReceived += rc;
}
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
}
Socket programming 33
Socket flow of events: Server application that uses AF_UNIX_CCSID address family
The first example uses the following sequence of API calls:
1. The socket() API returns a socket descriptor, which represents an endpoint. The statement also
identifies that the UNIX_CCSID address family with the stream transport (SOCK_STREAM) is used for
this socket. You can also use the socketpair() API to initialize a UNIX socket.
AF_UNIX or AF_UNIX_CCSID are the only address families to support the socketpair() API. The
socketpair() API returns two socket descriptors that are unnamed and connected.
2. After the socket descriptor is created, the bind() API gets a unique name for the socket.
The name space for UNIX domain sockets consists of path names. When a sockets program calls the
bind() API, an entry is created 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() API to remove
the directory entry when it ends.
3. The listen() allows the server to accept incoming client connections. In this example, the backlog is
set to 10. This means that the system queues 10 incoming connection requests before the system
starts rejecting the incoming requests.
4. The server uses the accept() API to accept an incoming connection request. The accept() call
blocks indefinitely, waiting for the incoming connection to arrive.
5. The recv() API receives data from the client application. In this example, the client sends 250 bytes
of data over. Thus, the SO_RCVLOWAT socket option can be used, which specifies that recv() is not
required to wake up until all 250 bytes of data have arrived.
6. The send() API echoes the data back to the client.
7. The close() API closes any open socket descriptors.
8. The unlink() API removes the UNIX path name from the file system.
/**************************************************************************/
/* This example program provides code for a server application for */
/* AF_UNIX_CCSID address family. */
/**************************************************************************/
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/unc.h>
Socket programming 35
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PATH "/tmp/server"
#define BUFFER_LENGTH 250
#define FALSE 0
void main()
{
/***********************************************************************/
/* Variable and structure definitions. */
/***********************************************************************/
int sd=-1, sd2=-1;
int rc, length;
char buffer[BUFFER_LENGTH];
struct sockaddr_unc serveraddr;
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of each of the socket descriptors is only done once at the */
/* very end of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor, which represents */
/* an endpoint. The statement also identifies that the UNIX_CCSID */
/* address family with the stream transport (SOCK_STREAM) will be */
/* used for this socket. */
/********************************************************************/
sd = socket(AF_UNIX_CCSID, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
break;
}
/********************************************************************/
/* After the socket descriptor is created, a bind() function gets a */
/* unique name for the socket. */
/********************************************************************/
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sunc_family = AF_UNIX_CCSID;
serveraddr.sunc_format = SO_UNC_USE_QLG;
serveraddr.sunc_qlg.CCSID = 500;
serveraddr.sunc_qlg.Path_Type = QLG_PTR_SINGLE;
serveraddr.sunc_qlg.Path_Length = strlen(SERVER_PATH);
serveraddr.sunc_path.p_unix = SERVER_PATH;
/********************************************************************/
/* 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 will queue 10 incoming connection */
/* requests before the system starts rejecting the incoming */
/* requests. */
/********************************************************************/
rc = listen(sd, 10);
if (rc< 0)
{
perror("listen() failed");
break;
}
/********************************************************************/
/* The server uses the accept() function to accept an incoming */
/* connection request. The accept() call will block indefinitely */
/* waiting for the incoming connection to arrive. */
/********************************************************************/
sd2 = accept(sd, NULL, NULL);
if (sd2 < 0)
{
perror("accept() failed");
/********************************************************************/
/* In this example we know that the client will send 250 bytes of */
/* data over. Knowing this, we can use the SO_RCVLOWAT socket */
/* option and specify that we don't want our recv() to wake up */
/* until all 250 bytes of data have arrived. */
/********************************************************************/
length = BUFFER_LENGTH;
rc = setsockopt(sd2, SOL_SOCKET, SO_RCVLOWAT,
(char *)&length, sizeof(length));
if (rc < 0)
{
perror("setsockopt(SO_RCVLOWAT) failed");
break;
}
/********************************************************************/
/* Receive that 250 bytes data from the client */
/********************************************************************/
rc = recv(sd2, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("recv() failed");
break;
}
/********************************************************************/
/* Echo the data back to the client */
/********************************************************************/
rc = send(sd2, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("send() failed");
break;
}
/********************************************************************/
/* Program complete */
/********************************************************************/
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
if (sd2 != -1)
close(sd2);
/***********************************************************************/
/* Remove the UNIX path name from the file system */
/***********************************************************************/
unlink(SERVER_PATH);
}
/**************************************************************************/
/* This example program provides code for a client application for */
/* AF_UNIX_CCSID address family. */
/**************************************************************************/
Socket programming 37
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <string.h>
#include <wcstr.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/unc.h>
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PATH "/tmp/server"
#define BUFFER_LENGTH 250
#define FALSE 0
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of the socket descriptor is only done once at the very end */
/* of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor, which represents */
/* an endpoint. The statement also identifies that the UNIX_CCSID */
/* address family with the stream transport (SOCK_STREAM) will be */
/* used for this socket. */
/********************************************************************/
sd = socket(AF_UNIX_CCSID, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
break;
}
/********************************************************************/
/* If an argument was passed in, use this as the server, otherwise */
/* use the #define that is located at the top of this program. */
/********************************************************************/
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sunc_family = AF_UNIX_CCSID;
if (argc > 1)
{
/* The argument is a UNICODE path name. Use the default format */
serveraddr.sunc_format = SO_UNC_DEFAULT;
wcscpy(serveraddr.sunc_path.wide, (wchar_t *) argv[1]);
}
else
{
/* The local #define is CCSID 500. Set the Qlg_Path_Name to use */
/* the character format */
serveraddr.sunc_format = SO_UNC_USE_QLG;
serveraddr.sunc_qlg.CCSID = 500;
serveraddr.sunc_qlg.Path_Type = QLG_CHAR_SINGLE;
serveraddr.sunc_qlg.Path_Length = strlen(SERVER_PATH);
strcpy((char *)&serveraddr.sunc_path, SERVER_PATH);
}
/********************************************************************/
/* Use the connect() function to establish a connection to the */
/* server. */
/********************************************************************/
rc = connect(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (rc < 0)
{
perror("connect() failed");
/********************************************************************/
/* Send 250 bytes of a's to the server */
/********************************************************************/
memset(buffer, 'a', sizeof(buffer));
rc = send(sd, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("send() failed");
break;
}
/********************************************************************/
/* In this example we know that the server is going to respond with */
/* the same 250 bytes that we just sent. Since we know that 250 */
/* bytes are going to be sent back to us, we can use the */
/* SO_RCVLOWAT socket option and then issue a single recv() and */
/* retrieve all of the data. */
/* */
/* The use of SO_RCVLOWAT is already illustrated in the server */
/* side of this example, so we will do something different here. */
/* The 250 bytes of the data may arrive in separate packets, */
/* therefore we will issue recv() over and over again until all */
/* 250 bytes have arrived. */
/********************************************************************/
bytesReceived = 0;
while (bytesReceived < BUFFER_LENGTH)
{
rc = recv(sd, & buffer[bytesReceived],
BUFFER_LENGTH - bytesReceived, 0);
if (rc < 0)
{
perror("recv() failed");
break;
}
else if (rc == 0)
{
printf("The server closed the connection\n");
break;
}
/*****************************************************************/
/* Increment the number of bytes that have been received so far */
/*****************************************************************/
bytesReceived += rc;
}
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
}
Asynchronous I/O
Asynchronous I/O APIs provide a method for threaded client/server models to perform highly concurrent
and memory-efficient I/O.
In previous threaded client/server models, typically two I/O models have prevailed. The first model
dedicates one thread per client connection. The first model consumes too many threads and might incur a
substantial sleep and wake-up cost. The second model minimizes the number of threads by issuing the
select() API on a large set of client connections and delegating a readied client connection or request
to a thread. In the second model, you must select or mark on each subsequent select, which might cause
a substantial amount of redundant work.
Socket programming 39
Asynchronous I/O and overlapped I/O resolve both these dilemmas by passing data to and from user
buffers after control has been returned to the user application. Asynchronous I/O notifies these worker
threads when data is available to be read or when a connection has become ready to transmit data.
Socket programming 41
• A buffer that was supplied on the original request
• The length of data that was processed to or from that buffer
• An indication of what type of I/O operation has been completed
• Application-defined handle that was passed on the initial I/O request
This application handle can be the socket descriptor identifying the client connection, or a pointer to
storage that contains extensive information about the state of the client connection. Since the operation
was completed and the application handle was passed, the worker thread determines the next step to
complete the client connection. Worker threads that process these completed asynchronous operations
can handle many different client requests and are not tied to just one. Because copying to and from user
buffers occurs asynchronously to the server processes, the wait time for client request diminishes. This
can be beneficial on systems where there are multiple processors.
#include <qsoasync.h>
struct Qso_OverlappedIO_t
{
Qso_DescriptorHandle_t descriptorHandle;
void *buffer;
size_t bufferLength;
int postFlag : 1;
int fillBuffer : 1;
int postFlagResult : 1;
int reserved1 : 29;
int returnValue;
int errnoValue;
int operationCompleted;
int secureDataTransferSize;
unsigned int bytesAvailable;
struct timeval operationWaitTime;
int postedDescriptor;
char reserved2[40];
}
Related reference
Example: Using asynchronous I/O
An application creates an I/O completion port using the QsoCreateIOCompletionPort() API. This API
returns a handle that can be used to schedule and wait for completion of asynchronous I/O requests.
Socket application design recommendations
Before working with a socket application, assess the functional requirements, goals, and needs of the
socket application. Also, consider the performance requirements and the system resource impacts of the
application.
Examples: Connection-oriented designs
You can design a connection-oriented socket server on the system in a number of ways. These example
programs can be used to create your own connection-oriented designs.
Example: Using signals with blocking socket APIs
When a process or an application becomes blocked, signals allow you to be notified. They also provide a
time limit for blocking processes.
Related information
gsk_secure_soc_startInit()--Start asynchronous operation to negotiate a secure session API
gsk_secure_soc_startRecv()--Start asynchronous receive operation on a secure session API
gsk_secure_soc_startSend()--Start asynchronous send operation on a secure session API
QsoCreateIOCompletionPort()--Create I/O Completion Port API
QsoDestroyIOCompletionPort()--Destroy I/O Completion Port API
QsoWaitForIOCompletion()--Wait for I/O Operation API
QsoStartAccept()--Start asynchronous accept operation API
Secure sockets
Currently, you have two methods to create secure socket applications on the IBM i operating system. The
SSL_ APIs and Global Security Kit (GSKit) APIs provide communications privacy over an open
communications network, which in most cases is the Internet.
These APIs allow client/server applications to communicate in a way that prevents eavesdropping,
tampering, and message forgery. Both the SSL_ APIs and Global Secure Toolkit (GSKit) APIs support
server and client authentication, and both allow an application to use the Secure Sockets Layer (SSL)
protocol. However, GSKit APIs are supported for all IBM systems, while the SSL_ APIs exist only in the
IBM i operating system. To enhance portability across systems, it is suggested that you use GSKit APIs
when developing applications for secure socket connections.
Socket programming 43
Table 12. Global Security Kit APIs
API Description
gsk_attribute_get_buffer() Obtains specific character string
information about a secure session or an
SSL environment, such as certificate store
file, certificate store password, application
ID, and ciphers.
gsk_attribute_get_cert_info() Obtains specific information about either
the server or client certificate for a secure
session or an SSL environment.
gsk_attribute_get_enum_value() Obtains values for specific enumerated data
for a secure session or an SSL environment.
gsk_attribute_get_numeric_value() Obtains specific numeric information about
a secure session or an SSL environment.
gsk_attribute_set_callback() Sets callback pointers to routines in the
user application. The application can then
use these routines for special purposes.
gsk_attribute_set_buffer() Sets a specified buffer attribute to a value
inside the specified secure session or an
SSL environment.
gsk_attribute_set_enum() Sets a specified enumerated type attribute
to an enumerated value in the secure
session or SSL environment.
gsk_attribute_set_numeric_value() Sets specific numeric information for a
secure session or an SSL environment.
gsk_environment_close() Closes the SSL environment and releases all
storage associated with the environment.
gsk_environment_init() Initializes the SSL environment after any
required attributes are set.
gsk_environment_open() Returns an SSL environment handle that
must be saved and used on subsequent gsk
calls.
gsk_secure_soc_close() Closes a secure session and free all the
associated resources for that secure
session.
gsk_secure_soc_init() Negotiates a secure session, using the
attributes set for the SSL environment and
the secure session.
gsk_secure_soc_misc() Performs miscellaneous APIs for a secure
session.
gsk_secure_soc_open() Obtains storage for a secure session, sets
default values for attributes, and returns a
handle that must be saved and used on
secure session-related API calls.
gsk_secure_soc_read() Receives data from a secure session.
An application that uses the sockets and GSKit APIs contains the following elements:
1. A call to socket() to obtain a socket descriptor.
2. A call to gsk_environment_open() to obtain a handle to an SSL environment.
3. One or more calls to gsk_attribute_set_xxxxx() to set attributes of the SSL environment. At a
minimum, either a call to gsk_attribute_set_buffer() to set the
GSK_OS400_APPLICATION_ID value or to set the GSK_KEYRING_FILE value. Only one of these
should be set. It is preferred that you use the GSK_OS400_APPLICATION_ID value. Also ensure that
you set the type of application (client or server), GSK_SESSION_TYPE, using
gsk_attribute_set_enum().
4. A call to gsk_environment_init() to initialize this environment for SSL processing and to
establish the SSL security information for all SSL sessions that run using this environment.
5. 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 enable a server to accept incoming
connection requests.
6. A call to gsk_secure_soc_open() to obtain a handle to a secure session.
7. One or more calls to gsk_attribute_set_xxxxx() to set attributes of the secure session. At a minimum,
a call to gsk_attribute_set_numeric_value() to associate a specific socket with this secure
session.
8. A call to gsk_secure_soc_init() to initiate the SSL handshake negotiation of the cryptographic
parameters.
Note: Typically, a server program must provide a certificate for an SSL handshake to succeed. A
server must also have access to the private key that is associated with the server certificate and the
key database file where the certificate is stored. In some cases, a client must also provide a
certificate during the SSL handshake processing. This occurs if the server, which the client is
connecting to, has enabled client authentication. The
gsk_attribute_set_buffer(GSK_OS400_APPLICATION_ID) or
gsk_attribute_set_buffer(GSK_KEYRING_FILE) API calls identify (though in dissimilar ways)
the key database file, from which the certificate and private key that are used during the handshake
are obtained.
9. Calls to gsk_secure_soc_read() and gsk_secure_soc_write() to receive and send data.
10. A call to gsk_secure_soc_close() to end the secure session.
11. A call to gsk_environment_close() to close the SSL environment.
12. A call to close() to destroy the connected socket.
Socket programming 45
Related reference
Example: GSKit secure server with asynchronous data receive
This example demonstrates how to establish a secure server using Global Security Kit (GSKit) APIs.
Example: GSKit secure server with asynchronous handshake
The gsk_secure_soc_startInit() API allows you to create secure server applications that can
handle requests asynchronously.
Example: Establishing a secure client with Global Security Kit APIs
This example demonstrates how to establish a client using the Global Security Kit (GSKit) APIs.
Related information
gsk_attribute_get_buffer()--Get character information about a secure session or an SSL environment API
gsk_attribute_get_cert_info()--Get information about a local or partner certificate API
gsk_attribute_get_enum()--Get enumerated information about a secure session or an SSL environment
API
gsk_attribute_get_numeric_value()--Get numeric information about a secure session or an SSL
environment API
gsk_attribute_set_callback()--Set callback pointers to routines in the user application API
gsk_attribute_set_buffer()--Set character information for a secure session or an SSL environment API
gsk_attribute_set_enum()--Set enumerated information for a secure session or an SSL environment API
gsk_attribute_set_numeric_value()--Set numeric information for a secure session or an SSL environment
API
gsk_environment_close()--Close an SSL environment API
gsk_environment_init()--Initialize an SSL environment API
gsk_environment_open()--Get a handle for an SSL environment API
gsk_secure_soc_close()--Close a secure session API
gsk_secure_soc_init()--Negotiate a secure session API
gsk_secure_soc_misc()--Perform miscellaneous functions for a secure session API
gsk_secure_soc_open()--Get a handle for a secure session API
gsk_secure_soc_startInit()--Start asynchronous operation to negotiate a secure session API
gsk_secure_soc_read()--Receive data on a secure session API
gsk_secure_soc_write()--Send data on a secure session API
gsk_secure_soc_startRecv()--Start asynchronous receive operation on a secure session API
gsk_secure_soc_startSend()--Start asynchronous send operation on a secure session API
gsk_strerror()--Retrieve GSKit runtime error message API
socket()--Create Socket API
bind()--Set Local Address for Socket API
connect()--Establish Connection or Destination Address API
listen()--Invite Incoming Connections Requests API
accept()--Wait for Connection Request and Make Connection API
close()--Close File or Socket Descriptor API
SSL_ APIs
Do not use the SSL_ APIs for any new applications. They are not able to support or be aware of many of
the new features of System SSL. The documentation remains for existing applications that are not yet
converted to GSKit.
Unlike GSKit APIs, SSL_ APIs only exist in the IBM i operating system. The following table describes the
SSL_ APIs that are supported in the IBM i implementation.
SSL_Init_Application() Initialize the current job for SSL and establish the
SSL security information for the current job.
Note: Either an SSL_Init() or
SSL_Init_Application() API must be
processed before SSL can be used.
An application that uses the sockets and SSL_ APIs contains the following elements:
• A call to socket() to obtain a socket descriptor.
• Either call SSL_Init() or SSL_Init_Application() to initialize the job environment for SSL
processing and to establish the SSL security information for all SSL sessions that run in the current job.
Only one of these APIs should be used. It is preferred that you use the SSL_Init_Application()
API.
• 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 enable a server to accept incoming connection
requests.
• A call to SSL_Create() to enable SSL support for the connected socket.
• A call to SSL_Handshake() to initiate the SSL handshake negotiation of the cryptographic parameters.
Note: Typically, a server program must provide a certificate for an SSL handshake to succeed. A server
must also have access to the private key that is associated with the server certificate and the key
database file where the certificate is stored. In some cases, a client must also provide a certificate
during the SSL handshake processing. This occurs if the server, which the client is connecting to, has
enabled client authentication. The SSL_Init() or SSL_Init_Application() APIs identify (though
in dissimilar ways) the key database file, from which the certificate and private key that are used during
the handshake are obtained.
• Calls to SSL_Read() and SSL_Write() to receive and send data.
• A call to SSL_Destroy() to disable SSL support for the socket.
Socket programming 47
• A call to close() to destroy the connected sockets.
Related information
socket()--Create Socket API
listen()--Invite Incoming Connections Requests API
bind()--Set Local Address for Socket API
connect()--Establish Connection or Destination Address API
accept()--Wait for Connection Request and Make Connection API
close()--Close File or Socket Descriptor API
SSL_Create()--Enable SSL Support for the Specified Socket Descriptor API
SSL_Destroy()--End SSL Support for the Specified SSL Session API
SSL_Handshake()--Initiate the SSL Handshake Protocol API
SSL_Init()--Initialize the Current Job for SSL API
SSL_Init_Application()--Initialize the Current Job for SSL Processing Based on the Application Identifier
API
SSL_Read()--Receive Data from an SSL-Enabled Socket Descriptor API
SSL_Write()--Write Data to an SSL-Enabled Socket Descriptor API
SSL_Strerror()--Retrieve SSL Runtime Error Message API
SSL_Perror()--Print SSL Error Message API
Socket programming 49
Table 14. Secure socket API error code messages (continued)
Return code Message ID Constant name
6014 CPDBCBC GSK_OS400_ERROR_INVALID_OVERLAPPEDIO_T
6015 CPDBCBD GSK_OS400_ERROR_INVALID_IOCOMPLETIONPORT
6016 CPDBCBE GSK_OS400_ERROR_BAD_SOCKET_DESCRIPTOR
6017 CPDBCBF GSK_OS400_ERROR_CERTIFICATE_REVOKED
6018 CPDBC87 GSK_OS400_ERROR_CRL_INVALID
6019 CPCBC88 GSK_OS400_ASYNCHRONOUS_SOC_INIT
0 CPCBC80 Successful return
-1 CPDBC81 SSL_ERROR_NO_CIPHERS
-2 CPDBC82 SSL_ERROR_NO_CERTIFICATE
-4 CPDBC84 SSL_ERROR_BAD_CERTIFICATE
-6 CPDBC86 SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE
-10 CPDBC8A SSL_ERROR_IO
-11 CPDBC8B SSL_ERROR_BAD_MESSAGE
-12 CPDBC8C SSL_ERROR_BAD_MAC
-13 CPDBC8D SSL_ERROR_UNSUPPORTED
-15 CPDBC84 SSL_ERROR_BAD_CERT (map to -4)
-16 CPDBC8B SSL_ERROR_BAD_PEER (map to -11)
-18 CPDBC92 SSL_ERROR_SELF_SIGNED
-21 CPDBC95 SSL_ERROR_BAD_STATE
-22 CPDBC96 SSL_ERROR_SOCKET_CLOSED
-23 CPDBC97 SSL_ERROR_NOT_TRUSTED_ROOT
-24 CPDBC98 SSL_ERROR_CERT_EXPIRED
-26 CPDBC9A SSL_ERROR_BAD_KEY_LEN_FOR_EXPORT
-91 CPDBCB1 SSL_ERROR_KEYPASSWORD_EXPIRED
-92 CPDBCB2 SSL_ERROR_CERTIFICATE_REJECTED
-93 CPDBCB3 SSL_ERROR_SSL_NOT_AVAILABLE
-94 CPDBCB4 SSL_ERROR_NO_INIT
-95 CPDBCB5 SSL_ERROR_NO_KEYRING
-97 CPDBCB7 SSL_ERROR_BAD_CIPHER_SUITE
-98 CPDBCB8 SSL_ERROR_CLOSED
-99 CPDBCB9 SSL_ERROR_UNKNOWN
-1009 CPDBCC9 SSL_ERROR_NOT_REGISTERED
-1011 CPDBCCB SSL_ERROR_NO_CERTIFICATE_AUTHORITIES
-9998 CPDBCD8 SSL_ERROR_NO_REUSE
Socket programming 51
Two actions are required on the secure client host to use a SOCKS server:
1. Configure a SOCKS server.
2. On the secure client system, define all outbound client TCP connections that are to be directed to the
SOCKS server on the client system.
To configure client SOCKS support, follow these steps:
a. From System i® Navigator, expand your system > Network > TCP/IP Configuration.
b. Right-click TCP/IP Configuration.
c. Click Properties.
d. Click the SOCKS tab.
e. Enter your connection information about the SOCKS page.
Note: The secure client SOCKS configuration data is saved in the QASOSCFG file in library QUSRSYS on
the secure client host system.
When configured, the system automatically directs certain outbound connections to the SOCKS server
you specified on the SOCKS page. You do not need to make any changes to the secure client application.
When it receives the request, the SOCKS server establishes a separate external TCP/IP connection to the
server in the less secure network. The SOCKS server then relays data between the internal and external
TCP/IP connections.
Note: The remote host on the less secure network connects directly to the SOCKS server. It does not have
direct access to the secure client.
Up to this point, outbound TCP connections that originate from the secure client have been addressed.
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. For Rbind() to
operate, the secure client must have previously issued a connect() call and the call must have resulted
in an outbound connection over the SOCKS server. The Rbind() inbound connection must be from the
same IP address that was addressed by the outbound connection that the connect() established.
The following figure shows a detailed overview of how sockets APIs interact with a SOCKS server
transparent to the application. In the example, the FTP client calls the Rbind() API instead of a bind()
API, because the FTP protocol allows the FTP server to establish a data connection when there is a
request from the FTP client to send files or data. It makes this call by recompiling the FTP client code with
the __Rbind preprocessor #define, which defines bind() to be Rbind(). Alternatively, an application
can explicitly code Rbind() in the pertinent source code. If an application does not require inbound
connections across a SOCKS server, Rbind() should not be used.
Thread safety
A function is considered threadsafe if you can start it simultaneously in multiple threads within the same
process. A function is threadsafe only if all the functions it calls are also threadsafe. Socket APIs consist
of system and network functions, which are both threadsafe.
All network functions with names that end in "_r" have similar semantics and are also threadsafe.
The other resolver routines are threadsafe with each other but they use the _res data structure. This data
structure is shared between all threads in a process and can be changed by an application during a
resolver call.
Related reference
Example: Using gethostbyaddr_r() for threadsafe network routines
This example program uses the gethostbyaddr_r() API. All other routines with names that end in _r
have similar semantics and are also threadsafe.
Example: Updating and querying DNS
This example shows how to query and update Domain Name System (DNS) records.
Nonblocking I/O
When an application issues one of the socket input APIs and there is no data to read, the API blocks and
does not return until there is data to read.
Similarly, an application can block on a socket output API when data cannot be sent immediately. Finally,
connect() and accept() can block while waiting for connection establishment with the partner's
programs.
Sockets provide a method that enables application programs to issue APIs that block so that the API
returns without delay. This is done by either calling fcntl() to turn on the O_NONBLOCK flag, or calling
Socket programming 55
writev()--Write to Descriptor Using Multiple Buffers API
Signals
An application program can request to be notified asynchronously (request that the system send a signal)
when a condition that the application is interested in occurs.
There are two asynchronous signals that sockets send to an application.
1. SIGURG is a signal that is sent when out-of-band (OOB) data is received on a socket for which the
concept of OOB data is supported. For example, a socket with an address family of AF_INET6 and a
type of SOCK_STREAM can be conditioned to send a SIGURG signal.
2. SIGIO is a signal that is sent when normal data, OOB data, error conditions, or just about anything
happens on any type of socket.
The application should ensure that it is able to handle receiving a signal before it requests the system to
send signals. This is done 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:
• Issuing a fcntl() call and specifying a process ID or a process group ID with the F_SETOWN
command.
• Issuing an ioctl() call and specifying the FIOSETOWN or the SIOCSPGRP command (request).
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 previously described for the SIGURG signal. This is to inform the system of
where the application wants the signal to be delivered. Second, the application must do either of the
following tasks:
• Issue the fcntl() call and specify the F_SETFL command with the FASYNC flag.
• Issue the ioctl() call and specify the FIOASYNC command.
This step requests the system to generate the SIGIO signal. Note that these steps can be done in any
order. Also note that 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() API. That is,
newly accepted sockets also have the same process ID or process group ID as well as the same
information with regard to sending the SIGIO signal.
A socket can also generate synchronous signals on error conditions. Whenever an application receives
[EPIPE] an errno on a socket API, a SIGPIPE signal is delivered to the process that issued the operation
receiving the errno value. On a Berkeley Socket Distribution (BSD) implementation, by default the
SIGPIPE signal ends the process that received the errno value. To remain compatible with previous
releases of the IBM i implementation, the IBM i implementation uses a default behavior of ignoring for the
SIGPIPE signal. This ensures that existing applications are not negatively affected by the addition of the
signals API.
When a signal is delivered to a process that is blocked on a sockets API, the API returns from the wait
with the [EINTR] errno value, allowing the application's signal handler to run. The APIs for which this
occur are:
• accept()
• connect()
• poll()
• read()
• readv()
• recv()
• recvfrom()
• recvmsg()
• select()
IP multicasting
IP multicasting allows an application to send a single IP datagram that a group of hosts in a network can
receive.
The hosts that are in the group can reside on a single subnet or on different subnets that multicast-
capable routers connect. Hosts can join and leave groups at any time. There are no restrictions on the
location or number of members in a host group. For AF_INET, a class D IP address in the range 224.0.0.1
to 239.255.255.255 identifies a host group. For AF_INET6, an IPv6 address starting with FF00::/8
identifies the address as a multicast address. Refer to RFC 3513: "Internet Protocol Version 6 (IPv6)
Addressing Architecture" for more information.
You can currently use IP multicasting with AF_INET and AF_INET6 address families.
An application program can send or receive multicast datagrams using the Sockets API and
connectionless, SOCK_DGRAM type sockets. Multicasting is a one-to-many transmission method.
Connection-oriented sockets of type SOCK_STREAM cannot be used for multicasting. When a socket of
type SOCK_DGRAM is created, an application can use the setsockopt() API to control the multicast
characteristics associated with that socket. The setsockopt() API accepts the following IPPROTO_IP
level flags:
• IP_ADD_MEMBERSHIP: Joins the multicast group specified
Socket programming 57
• IP_DROP_MEMBERSHIP: Leaves the multicast group specified
• IP_MULTICAST_IF: Sets the interface over which outgoing multicast datagrams should be sent
• IP_MULTICAST_TTL: Sets the Time To Live (TTL) in the IP header for outgoing multicast datagrams
• IP_MULTICAST_LOOP: Specifies whether a copy of an outgoing multicast datagram should be delivered
to the sending host as long as it is a member of the multicast group
The setsockopt() API also accepts the following IPPROTO_IPv6 level flags:
• IPv6_MULTICAST_IF: Sets the interface over which outgoing multicast datagrams are sent
• IPv6_MULTICAST_HOPS: Sets the hop limit values that are used for subsequent multicast packets sent
by a socket
• IPv6_MULTICAST_LOOP: Specifies whether a copy of an outgoing multicast datagram should be
delivered to the sending host as long as it is a member of the multicast group
• IPv6_JOIN_GROUP: Joins the multicast group specified
• IPv6_LEAVE_GROUP: Leaves the multicast group specified
Related reference
Examples: Using multicasting with AF_INET
With IP multicasting, an application can send a single IP datagram that a group of hosts in a network can
receive.
Related information
setsockopt()--Set Socket Options API
Out-of-band data
Out-of-band (OOB) data is user-specific data that only has meaning for connection-oriented (stream)
sockets.
Stream data is generally received in the same order it is sent. OOB data is received independent of its
position in the stream (independent of the order in which it was sent). This is possible because the data is
marked in such a way that, when it is sent from program A to program B, program B is notified of its
arrival.
OOB data is supported on AF_INET (SOCK_STREAM) and AF_INET6 (SOCK_STREAM) only.
OOB data is sent by specifying the MSG_OOB flag on the send(), sendto(), and sendmsg() APIs.
Socket programming 59
I/O multiplexing—select()
Because asynchronous I/O provides a more efficient way to maximize your application resources, it is
recommended that you use asynchronous I/O APIs rather than the select() API. However, your
specific application design might allow select() to be used.
Like asynchronous I/O, the select() API creates a common point to wait for multiple conditions at the
same time. However, select() allows an application to specify sets of descriptors to see if the following
conditions exist:
• There is data to be read.
• Data can be written.
• An exception condition is present.
The descriptors that can be specified in each set can be socket descriptors, file descriptors, or any other
object that is represented by a descriptor.
The select() API also allows the application to specify if it wants to wait for data to become available.
The application can specify how long to wait.
Related reference
Example: Nonblocking I/O and select()
This sample program illustrates a server application that uses nonblocking and the select() API.
Environment variables
You can use environment variables to override default initialization of resolver functions.
Environment variables are only checked after a successful call to res_init() or res_ninit(). So if
the structure has been manually initialized, environment variables are ignored. Also note that the
structure is only initialized once so later changes to the environment variables are ignored.
Note: The name of the environment variable must be capitalized. The string value might be mixed case.
Japanese systems using CCSID 290 should use uppercase characters and numbers only in both
environment variables names and values. The list contains the descriptions of environment variables that
can be used with the res_init() and res_ninit() APIs.
Socket programming 61
LOCALDOMAIN
Set this environment variable to a space-separated list of up to six search domains with a total of 256
characters (including spaces). This overrides the configured search list (struct state.defdname and struct
state.dnsrch). If a search list is specified, the default local domain is not used on queries.
RES_OPTIONS
The RES_OPTIONS environment variable allows certain internal resolver variables to be modified. The
environment variable can be set to one or more of the following space-separated options:
• NDOTS: n Sets a threshold for the number of dots that must appear in a name given to res_query()
before an initial absolute query is made. The default for n is 1, meaning that if there are any dots in a
name, the name is tried first as an absolute name before any search list elements are appended to it.
• TIMEOUT: n Sets the amount of time (in seconds) that the resolver waits for a response from a remote
name server before giving up and trying the query again.
• ATTEMPTS: n Sets the number of queries that the resolver sends to a given nameServer before giving
up and trying the next listed name server.
• ROTATE: Sets RES_ROTATE in _res.options, which rotates the selection of nameServers from among
those listed. This has the effect of spreading the query load among all listed servers, rather than having
all clients try the first listed server first every time.
• NO-CHECK-NAMES: Sets RES_NOCHECKNAME in _res.options, which disables the modern BIND
checking of incoming host names and mail names for invalid characters such as underscore (_), non-
ASCII, or control characters.
QIBM_BIND_RESOLVER_FLAGS
Set this environment variable to a space separated list of resolver option flags. This overrides the
RES_DEFAULT options (struct state.options) and system configured values (Change TCP/IP Domain -
CHGTCPDMN). The state options structure is initialized normally, using RES_DEFAULT, OPTIONS
environment values and CHGTCPDMN configured values. Then this environment variable is used to
override those defaults. The flags named in this environment variable might be prepended with a '+', '-' or
'NOT_' to set ('+') or reset ('-','NOT_') the value.
For example, to turn on RES_NOCHECKNAME and turn off RES_ROTATE, use the following command from
a character-based interface:
or
QIBM_BIND_RESOLVER_SORTLIST
Set this environment variable to a space-separated list of up to ten IP addresses/mask pairs in dotted
decimal format (9.5.9.0/255.255.255.0) to create a sort list (struct state.sort_list).
Related information
res_init()
res_ninit()
res_query()
Data caching
Data caching of responses to Domain Name System (DNS) queries is done by IBM i sockets in an effort to
lessen the amount of network traffic. The cache is added to and updated as needed.
If RES_AAONLY (authoritative answers only) is set in _res.options, the query is always sent on the
network. In this case, the cache is never checked for the answer. If RES_AAONLY is not set, the cache is
Socket programming 63
Berkeley Software Distribution compatibility
Sockets is a Berkeley Software Distribution (BSD) interface.
The semantics, such as the return codes that an application receives and the arguments available on
supported functions, are BSD semantics. Some BSD semantics, however, are not available in the IBM i
implementation, and changes might need to be made to a typical BSD socket application in order for it to
run on the system.
The following list summarizes the differences between the IBM i implementation and the BSD
implementation.
/etc/hosts, /etc/services, /etc/networks, and /etc/protocols
For these files, the IBM i implementation supplies the following database files.
/etc/resolv.conf
The IBM i implementation requires that this information is configured using the TCP/IP properties
page in IBM Navigator for i. To access the TCP/IP properties page, complete the following steps:
1. From IBM Navigator for i, expand IBM i Management > Network > All Tasks > TCP/IP
Configuration.
2. Click TCP/IP Configuration Properties.
Saving and Restoring TCP/IP configuration information
The Retrieve TCP/IP Information (RTVTCPINF) command gathers key TCP/IP configuration
information from the default system locations and places it in the library that is specified for the
Library (LIB) parameter. The gathered TCP/IP configuration information can be used by the Update
TCP/IP Information (UPDTCPINF) command to reset or restore the TCP/IP configuration on a system.
bind()
On a BSD system, a client can create an AF_UNIX socket using socket(), connect to a server using
connect(), and then bind a name to its socket using bind(). The IBM i implementation does not
support this scenario (the bind() fails).
close()
The IBM i implementation supports the linger timer for the close() API, except for AF_INET sockets
over Systems Network Architecture (SNA). Some BSD implementations do not support the linger
timer for the close() API.
connect()
On a BSD system, if a connect() is issued against a socket that was previously connected to an
address and is using a connectionless transport service, and an invalid address or an invalid address
length is used, the socket is no longer connected. The IBM i implementation does not support this
scenario (the connect() fails and the socket is still connected).
A connectionless transport socket for which a connect() has been issued can be disconnected by
setting the address_length parameter to zero and issuing another connect().
Socket programming 65
example, address a.b.c.e now appears on the getsockname() API instead of INADDR_ANY, and the
SO_REUSEADDR option must be used to bind any other sockets to the same port number with an
address of a.b.c.e.
In contrast, in this example, the IBM i implementation does not change the local address from
INADDR_ANY to a.b.c.e. The getsockname() API continues to return INADDR_ANY after the
connection is performed.
SO_SNDBUF and SO_RCVBUF options
The values set for SO_SNDBUF and SO_RCVBUF on a BSD system provide a greater level of control
than on an IBM i implementation. On an IBM i implementation, these values are taken as advisory
values.
Related concepts
How sockets work
Sockets are commonly used for client and server interaction. Typical system configuration places the
server on one machine, with the clients on other machines. The clients connect to the server, exchange
information, and then disconnect.
Related reference
Example: Using signals with blocking socket APIs
When a process or an application becomes blocked, signals allow you to be notified. They also provide a
time limit for blocking processes.
Related information
accept()--Wait for Connection Request and Make Connection API
--Send a Message Over a Socket API
connect()--Establish Connection or Destination Address API
recvfrom()--Receive Data API
recvmsg()--Receive a Message Over a Socket API
bind()--Set Local Address for Socket API
getsockname()--Retrieve Local Address of Socket API
socket()--Create Socket API
listen()--Invite Incoming Connections Requests API
ioctl()--Perform I/O Control Request API
getpeername()--Retrieve Destination Address of Socket API
close()--Close File or Socket Descriptor API
RTVTCPINF--Retrieve TCP/IP Information
UPDTCPINF--Update TCP/IP Information
UNIX 98 compatibility
Created by The Open Group, a consortium of developers and venders, UNIX 98 improved the inter-
operability of the UNIX operating system while incorporating much of the Internet-related function for
which UNIX had become known.
IBM i sockets provide programmers the ability to write socket applications that are compatible with UNIX
98 operating environment. Currently, IBM supports two versions of most sockets APIs. The base IBM i
socket APIs use Berkeley Socket Distribution (BSD) 4.3 structures and syntax. The other uses syntax and
structures compatible with BSD 4.4 and the UNIX 98 programming interface specifications. You can
select the UNIX 98 compatible interface by defining the _XOPEN_SOURCE macro to a value of 520 or
greater.
Table 15. Comparison of BSD 4.3 and UNIX 98/BSD 4.4 socket address structure
BSD 4.3 structure BSD 4.4/ UNIX 98 compatible structure
sockaddr address structure
API differences
When you develop in ILE-based languages and an application is compiled with the _XOPEN_SOURCE
macro, some sockets APIs are mapped to internal names. These internal names provide the same
function as the original API. The table lists these affected APIs. If you are writing socket applications in
some other C-based language, you can write directly to the internal name of these APIs. Use the link to
the original API to see usage notes and details for both versions of these APIs.
Socket programming 67
Table 16. API and UNIX 98 equivelant name (continued)
API name Internal name
endhostent() qso_endhostent98()
endnetent() qso_endnetent98()
endprotoent() qso_endprotoent98()
endservent() qso_endservent98()
getaddrinfo() qso_getaddrinfo98()
gethostbyaddr() qso_gethostbyaddr98()
gethostbyaddr_r() qso_gethostbyaddr_r98()
gethostname() qso_gethostname98()
gethostname_r() qso_gethostname_r98()
gethostbyname() qso_gethostbyname98()
gethostent() qso_gethostent98()
getnameinfo() qso_getnameinfo98()
getnetbyaddr() qso_getnetbyaddr98()
getnetbyname() qso_getnetbyname98()
getnetent() qso_getnetent98()
getpeername() qso_getpeername98()
getprotobyname() qso_getprotobyname98()
getprotobynumber() qso_getprotobynumber98()
getprotoent() qso_getprotoent98()
getsockname() qso_getsockname98()
getsockopt() qso_getsockopt98()
getservbyname() qso_getservbyname98()
getservbyport() qso_getservbyport98()
getservent() qso_getservent98()
getsourcefilter() qso_getsourcefilter98()
inet_addr() qso_inet_addr98()
inet_lnaof() qso_inet_lnaof98()
inet_makeaddr() qso_inet_makeaddr98()
inet_netof() qso_inet_netof98()
inet_network() qso_inet_network98()
inet6_is_srcaddr() qso_inet6_is_srcaddr98()
listen() qso_listen98()
Rbind() qso_Rbind98()
recv() qso_recv98()
Related concepts
How sockets work
Sockets are commonly used for client and server interaction. Typical system configuration places the
server on one machine, with the clients on other machines. The clients connect to the server, exchange
information, and then disconnect.
Related information
accept()--Wait for Connection Request and Make Connection API
accept_and_recv()
connect()--Establish Connection or Destination Address API
--Send a Message Over a Socket API
recvfrom()--Receive Data API
recvmsg()--Receive a Message Over a Socket API
Rbind()--Set Remote Address for Socket API
recv()--Receive Data API
bind()--Set Local Address for Socket API
getsockname()--Retrieve Local Address of Socket API
socket()--Create Socket API
socketpair()--Create a Pair of Sockets API
listen()--Invite Incoming Connections Requests API
ioctl()--Perform I/O Control Request API
getpeername()--Retrieve Destination Address of Socket API
close()--Close File or Socket Descriptor API
endhostent()
endnetent()
endprotoent()
endservent()
gethostbyname()--Get Host Information for Host Name API
Socket programming 69
getaddrinfo()--Get Address Information API
gethostbyaddr()--Get Host Information for IP Address API
getnameinfo()--Get Name Information for Socket Address API
gethostname()
gethostent()
getnetbyaddr()
getnetbyname()
getnetent()
getprotobyname()
getprotobynumber()
getprotoent()
getsockopt()
getservbyname()
getservbyport()
getservent()
inet_addr()
inet_1naof()
inet_makeaddr()
inet_netof()
inet_network()
send()--Send Data API
sendto()--Send Data API
sethostent()
setnetent()
setprotoent()
setservent()
setsockopt()--Set Socket Options API
Socket programming 71
A server can use the socketpair() API to create a pair of AF_UNIX sockets, use the sendmsg()
API to send a descriptor over one of the AF_UNIX sockets created by socketpair(), and then call
spawn() to create a child job that inherits the other end of the socket pair. The child job calls
recvmsg() to receive the descriptor that the server passed. The child job was not active when the
server called sendmsg().
Pass more than one descriptor at a time
The givedescriptor() and takedescriptor() APIs allow only one descriptor to be passed at a
time. The sendmsg() and recvmsg() APIs can be used to pass an array of descriptors.
Related reference
Example: Passing descriptors between processes
These examples demonstrate how to design a server program using the sendmsg() and recvmsg()
APIs to handle incoming connections.
Related information
socketpair()--Create a Pair of Sockets API
Notes:
1. By default, the sockets APIs accepting connections silently ignore rejected connections and wait for
the next incoming connection. To give an application the ability to be informed about rejected
connections, a socket option is provided. The socket option is enabled by setsockopt() with a level
of SOL_SOCKET and option name SO_ACCEPTEPERM. When the socket option is enabled, sockets
APIs accepting connections fail with EPERM for each incoming connection rejected by the user exit
program registered for QIBM_QSO_ACCEPT.
/******************************************************************/
/* Sample User Exit Program for QIBM_QSO_ACCEPT */
/* */
/* Exit Point Name : QIBM_QSO_ACCEPT */
/* */
/* Description : The following ILE C language program */
/* will reject all incoming connections to */
/* the server listening on port 12345 coming */
/* from the remote IP address of '192.0.2.1' or */
/* '2001:DB8::1' between the hours of 12 A.M. */
/* and 4 A.M. */
/******************************************************************/
#include <stdio.h>
#include <string.h>
#include <esoextpt.h> /* Exit program formats */
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
Qso_ACPT0100_Format_t input; /* input format */
struct in_addr addr4;
struct in6_addr addr6;
void *addr, *compareaddr;
char return_code;
int comparelen, port;
/****************************************************************/
/* Initialize the address to compare 192.0.2.1 and 2001:DB8::1 */
/****************************************************************/
inet_pton(AF_INET, "192.0.2.1", &addr4);
inet_pton(AF_INET6, "2001:DB8::1", &addr6);
/****************************************************************/
/* By default allow the connection. */
/****************************************************************/
return_code = '0';
/****************************************************************/
/* Copy format parameter to local storage. */
/****************************************************************/
memcpy(&input, (Qso_ACPT0100_Format_t *) argv[1],
sizeof(Qso_ACPT0100_Format_t));
/****************************************************************/
/* Determine if we have an IPv4 or IPv6 address */
/****************************************************************/
if(input.Local_Incoming_Address_Length ==
sizeof(struct sockaddr_in))
{
compareaddr = &addr4;
Socket programming 73
comparelen = sizeof(addr4);
addr = &input.Remote_Address.sinstruct.sin_addr;
port = input.Local_Incoming_Address.sinstruct.sin_port;
}
else
{
compareaddr = &addr6;
comparelen = sizeof(addr6);
addr = &input.Remote_Address.sin6struct.sin6_addr;
port = input.Local_Incoming_Address.sin6struct.sin6_port;
}
/**************************************************************/
/* If the local port is 12345 and the incoming connection is */
/* from 192.0.2.1 or 2001:DB8::1 */
/**************************************************************/
if(port == 12345 && (memcmp(addr, compareaddr, comparelen) == 0))
{
/************************************************************/
/* And the time is between 12 A.M. and 4 A.M. */
/* Reject the connection. */
/************************************************************/
if(IsTimeBetweenMidnightAnd4AM())
return_code = '1';
}
*argv[2] = return_code;
return 0;
}
Situation
Suppose that you are a socket programmer who works for an application development company that
specializes in socket applications for the IBM i. operating system. To keep ahead of its competitors, your
company has decided to develop a suite of applications that use the AF_INET6 address family, which
accept connections from IPv4 and IPv6. You want to create an application that processes requests from
both IPv4 and IPv6 nodes. You know that the IBM i operating system supports the AF_INET6 address
family sockets, which provides interoperability with AF_INET address family sockets. You also know that
you can accomplish this by using an IPv4-mapped IPv6 address format.
Scenario objectives
This scenario has the following objectives and goals:
1. Create a server application that accepts and processes requests from IPv6 and IPv4 clients
2. Create a client application that requests data from an IPv4 or IPv6 server application
Prerequisite steps
Before developing your application that meets these objectives, complete the following tasks:
1. Install QSYSINC library. This library provides necessary header files that are needed when compiling
socket applications.
2. Install the ILE C licensed program (5770-WDS option 51).
3. Install and configure an Ethernet card. For information about Ethernet options, see the Ethernet topic
in the information center.
4. Set up TCP/IP and IPv6 network. Refer to the information about configuring TCP/IP and configuring
IPv6.
Related reference
Using AF_INET6 address family
AF_INET6 sockets provide support for Internet Protocol version 6 (IPv6) 128 bit (16 byte) address
structures. Programmers can write applications using the AF_INET6 address family to accept client
requests from either IPv4 or IPv6 nodes, or from IPv6 nodes only.
Related information
Ethernet
Configuring TCP/IP for the first time
Configuring IPv6
Socket programming 75
Socket flow of events: Server application that accepts requests from both IPv4 and IPv6 clients
This flow describes each of the API calls and what they do within the socket application that accepts
requests from both IPv4 and IPv6 clients.
1. The socket() API specifies a socket descriptor that creates an endpoint. It also specifies the
AF_INET6 address family, which supports IPv6, and the TCP transport (SOCK_STREAM) is used for this
socket.
2. The setsockopt() API allows an application to reuse the local address when the server is restarted
before the required wait time expires.
3. A bind() API supplies a unique name for the socket. In this example, the programmer sets the
address to in6addr_any, which (by default) allows connections to be established from any IPv4 or IPv6
client that specifies port 3005 (that is, the bind is done to both the IPv4 and IPv6 port spaces).
Note: If the server only needs to handle IPv6 clients, then IPv6_ONLY socket option can be used.
4. The listen() API allows the server to accept incoming client connections. In this example, the
programmer sets the backlog to 10, which allows the system to queue ten connection requests before
the system starts rejecting incoming requests.
5. The server uses the accept() API to accept an incoming connection request. The accept() call
blocks indefinitely, waiting for the incoming connection to arrive from an IPv4 or IPv6 client.
6. The getpeername() API returns the client's address to the application. If the client is an IPv4 client,
the address is shown as an IPv4–mapped IPv6 address.
7. The recv() API receives 250 bytes of data from the client. In this example, the client sends 250
bytes of data over. Knowing this, the programmer uses the SO_RCVLOWAT socket option and specifies
that the recv() API to not wake up until all 250 bytes of data have arrived.
Note: By using the examples, you agree to the terms of the “Code license and disclaimer information” on
page 177.
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PORT 3005
#define BUFFER_LENGTH 250
#define FALSE 0
void main()
{
/***********************************************************************/
/* Variable and structure definitions. */
/***********************************************************************/
int sd=-1, sdconn=-1;
int rc, on=1, rcdsize=BUFFER_LENGTH;
char buffer[BUFFER_LENGTH];
struct sockaddr_in6 serveraddr, clientaddr;
int addrlen=sizeof(clientaddr);
char str[INET6_ADDRSTRLEN];
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of each of the socket descriptors is only done once at the */
/* very end of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor, which represents */
/* an endpoint. Get a socket for address family AF_INET6 to */
Socket programming 77
/* prepare to accept incoming connections on. */
/********************************************************************/
if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
{
perror("socket() failed");
break;
}
/********************************************************************/
/* The setsockopt() function is used to allow the local address to */
/* be reused when the server is restarted before the required wait */
/* time expires. */
/********************************************************************/
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,
(char *)&on,sizeof(on)) < 0)
{
perror("setsockopt(SO_REUSEADDR) failed");
break;
}
/********************************************************************/
/* After the socket descriptor is created, a bind() function gets a */
/* unique name for the socket. In this example, the user sets the */
/* address to in6addr_any, which (by default) allows connections to */
/* be established from any IPv4 or IPv6 client that specifies port */
/* 3005. (that is, the bind is done to both the IPv4 and IPv6 TCP/IP */
/* stacks). This behavior can be modified using the IPPROTO_IPV6 */
/* level socket option IPV6_V6ONLY if required. */
/********************************************************************/
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin6_family = AF_INET6;
serveraddr.sin6_port = htons(SERVER_PORT);
/********************************************************************/
/* Note: applications use in6addr_any similarly to the way they use */
/* INADDR_ANY in IPv4. A symbolic constant IN6ADDR_ANY_INIT also */
/* exists but can only be used to initialize an in6_addr structure */
/* at declaration time (not during an assignment). */
/********************************************************************/
serveraddr.sin6_addr = in6addr_any;
/********************************************************************/
/* Note: the remaining fields in the sockaddr_in6 are currently not */
/* supported and should be set to 0 to ensure upward compatibility. */
/********************************************************************/
if (bind(sd,
(struct sockaddr *)&serveraddr,
sizeof(serveraddr)) < 0)
{
perror("bind() failed");
break;
}
/********************************************************************/
/* 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 will queue 10 incoming connection */
/* requests before the system starts rejecting the incoming */
/* requests. */
/********************************************************************/
if (listen(sd, 10) < 0)
{
perror("listen() failed");
break;
}
/********************************************************************/
/* The server uses the accept() function to accept an incoming */
/* connection request. The accept() call will block indefinitely */
/* waiting for the incoming connection to arrive from an IPv4 or */
/* IPv6 client. */
/********************************************************************/
if ((sdconn = accept(sd, NULL, NULL)) < 0)
{
perror("accept() failed");
break;
}
else
{
/*****************************************************************/
/* Display the client address. Note that if the client is */
/********************************************************************/
/* In this example we know that the client will send 250 bytes of */
/* data over. Knowing this, we can use the SO_RCVLOWAT socket */
/* option and specify that we don't want our recv() to wake up */
/* until all 250 bytes of data have arrived. */
/********************************************************************/
if (setsockopt(sdconn, SOL_SOCKET, SO_RCVLOWAT,
(char *)&rcdsize,sizeof(rcdsize)) < 0)
{
perror("setsockopt(SO_RCVLOWAT) failed");
break;
}
/********************************************************************/
/* Receive that 250 bytes of data from the client */
/********************************************************************/
rc = recv(sdconn, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("recv() failed");
break;
}
/********************************************************************/
/* Echo the data back to the client */
/********************************************************************/
rc = send(sdconn, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("send() failed");
break;
}
/********************************************************************/
/* Program complete */
/********************************************************************/
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
if (sdconn != -1)
close(sdconn);
}
Related reference
Examples: Connection-oriented designs
You can design a connection-oriented socket server on the system in a number of ways. These example
programs can be used to create your own connection-oriented designs.
Example: IPv4 or IPv6 client
This sample program can be used with the server application that accepts requests from either IPv4 or
IPv6 clients.
Example: Generic client
Socket programming 79
This example contains the code for a common client job. The client job does a socket(), connect(),
send(), recv(), and close() operation.
Related information
socket()--Create Socket API
setsockopt()--Set Socket Options API
bind()--Set Local Address for Socket API
listen()--Invite Incoming Connections Requests API
accept()--Wait for Connection Request and Make Connection API
getpeername()--Retrieve Destination Address of Socket API
recv()--Receive Data API
send()--Send Data API
close()--Close File or Socket Descriptor API
inet_pton()
getaddrinfo()--Get Address Information API
connect()--Establish Connection or Destination Address API
/**************************************************************************/
/* This is an IPv4 or IPv6 client. */
/**************************************************************************/
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define BUFFER_LENGTH 250
#define FALSE 0
#define SERVER_NAME "ServerHostName"
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of the socket descriptor is only done once at the very end */
/* of the program along with the free of the list of addresses. */
/***********************************************************************/
do
{
/********************************************************************/
hints.ai_family = AF_INET6;
hints.ai_flags |= AI_NUMERICHOST;
}
}
/********************************************************************/
/* Get the address information for the server using getaddrinfo(). */
/********************************************************************/
rc = getaddrinfo(server, servport, &hints, &res);
if (rc != 0)
{
printf("Host not found --> %s\n", gai_strerror(rc));
if (rc == EAI_SYSTEM)
perror("getaddrinfo() failed");
break;
}
/********************************************************************/
/* The socket() function returns a socket descriptor, which represents */
/* an endpoint. The statement also identifies the address family, */
/* socket type, and protocol using the information returned from */
/* getaddrinfo(). */
/********************************************************************/
sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sd < 0)
{
perror("socket() failed");
break;
}
/********************************************************************/
/* Use the connect() function to establish a connection to the */
/* server. */
/********************************************************************/
rc = connect(sd, res->ai_addr, res->ai_addrlen);
if (rc < 0)
{
/*****************************************************************/
/* Note: the res is a linked list of addresses found for server. */
/* If the connect() fails to the first one, subsequent addresses */
/* (if any) in the list can be tried if required. */
/*****************************************************************/
perror("connect() failed");
break;
}
/********************************************************************/
/* Send 250 bytes of a's to the server */
/********************************************************************/
memset(buffer, 'a', sizeof(buffer));
rc = send(sd, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("send() failed");
Socket programming 81
break;
}
/********************************************************************/
/* In this example we know that the server is going to respond with */
/* the same 250 bytes that we just sent. Since we know that 250 */
/* bytes are going to be sent back to us, we can use the */
/* SO_RCVLOWAT socket option and then issue a single recv() and */
/* retrieve all of the data. */
/* */
/* The use of SO_RCVLOWAT is already illustrated in the server */
/* side of this example, so we will do something different here. */
/* The 250 bytes of the data may arrive in separate packets, */
/* therefore we will issue recv() over and over again until all */
/* 250 bytes have arrived. */
/********************************************************************/
while (bytesReceived < BUFFER_LENGTH)
{
rc = recv(sd, & buffer[bytesReceived],
BUFFER_LENGTH - bytesReceived, 0);
if (rc < 0)
{
perror("recv() failed");
break;
}
else if (rc == 0)
{
printf("The server closed the connection\n");
break;
}
/*****************************************************************/
/* Increment the number of bytes that have been received so far */
/*****************************************************************/
bytesReceived += rc;
}
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
/***********************************************************************/
/* Free any results returned from getaddrinfo */
/***********************************************************************/
if (res != NULL)
freeaddrinfo(res);
}
Related reference
Example: Accepting connections from both IPv6 and IPv4 clients
This example program demonstrates how to create a server/client model that accepts requests from both
IPv4 (those socket applications that use the AF_INET address family) and IPv6 (those applications that
use the AF_INET6 address family).
Example: Generic client
This example contains the code for a common client job. The client job does a socket(), connect(),
send(), recv(), and close() operation.
Socket programming 83
Table 18. Socket design applications (continued)
Recommendation Reason Best used in
When using select(), try to When there are a large number of Applications that have a large(>
avoid a large number of descriptors in a read, write or 50) number of descriptors active
descriptors in the read, write or exception set, considerable for select().
exception set. redundant work occurs each time
select() is called. As soon as a
Note: If you have a large number
select() is satisfied the actual
of descriptors being used for
socket function must still be
select() processing see the
done, that is, a read or write or
asynchronous I/O
accept must still be performed.
recommendation above.
Asynchronous I/O APIs combine
the notification that something
has occurred on a socket with the
actual I/O operation.
Save your copy of the read, write This saves the overhead of Any socket application where you
and exception sets before using rebuilding the read, write, or are using select() with a large
select() to avoid rebuilding the exception sets every time you number of socket descriptors
sets for every time you must plan to issue the select(). enabled for read, write or
reissue the select(). exception processing.
Do not use select() as a timer. Better timer response and less Any socket application where you
Use sleep() instead. system overhead. are using select() just as a
timer.
Note: If granularity of the
sleep() timer is not adequate,
you might need to use select()
as a timer. In this case, set
maximum descriptor to 0 and the
read, write, and exception set to
NULL.
If your socket application has If you allocate a descriptor Any application or process where
increased the maximum number outside the range of the read, you use DosSetRelMaxFH() and
of file and socket descriptors write or exception set, as select().
allowed per process using specified by FD_SETSIZE, then
DosSetRelMaxFH() and you are you can overwrite and destroy
using select() in this same storage. Ensure your set sizes are
application, be careful of the at least large enough to handle
affect this new maximum value whatever the maximum number
has on the size of the read, write of descriptors are set for the
and exception sets used for process and the maximum
select() processing. descriptor value specified on the
select() API.
Set all socket descriptors in the This allows you to minimize the Any socket application where you
read or write sets to nonblocking. number of select() calls when are using select().
When a descriptor becomes data is still available to be
enabled for read or write, loop processed or read on a
and consume or send all of the descriptor.
data until EWOULDBLOCK is
returned.
Only specify the sets that you Most applications do not need to Any socket application where you
need to use for select() specify the exception set or write are using select().
processing. set.
Use GSKit APIs instead of Both the Global Security Kit Any socket application that
SSL_APIs. (GSKit) and SSL_ APIs allow you needs to be enabled for SSL or
to develop secure AF_INET or TLS processing.
AF_INET6, SOCK_STREAM
socket applications. Because the
GSKit APIs are supported across
IBM systems, they are the
preferred APIs to secure an
application. The SSL_ APIs exist
only in the IBM i operating
system.
Avoid using signals. The performance overhead of Any socket application that uses
signals (on all platforms, not just signals.
the IBM i platform) is expensive.
It is better to design your socket
application to use Asynchronous
I/O or select() APIs.
Use protocol independent Even if you are not yet ready to Any AF_INET or AF_INET6
routines when available, such as support IPv6, use these APIs, application that uses network
inet_ntop(), inet_pton(), (instead of inet_ntoa(), routines.
getaddrinfo(), and inet_addr(),
getnameinfo(). gethostbyname() and
gethostbyaddr()) to prepare
you for easier migration.
Use sockaddr_storage to declare Simplifies writing code portable Any socket application that
storage for any address family across multiple address families stores addresses.
address. and platforms. Declares enough
storage to hold the largest
address family and ensures the
correct boundary alignment.
Related concepts
Asynchronous I/O
Asynchronous I/O APIs provide a method for threaded client/server models to perform highly concurrent
and memory-efficient I/O.
Related reference
Example: Using asynchronous I/O
An application creates an I/O completion port using the QsoCreateIOCompletionPort() API. This API
returns a handle that can be used to schedule and wait for completion of asynchronous I/O requests.
Example: Nonblocking I/O and select()
This sample program illustrates a server application that uses nonblocking and the select() API.
Example: Passing descriptors between processes
These examples demonstrate how to design a server program using the sendmsg() and recvmsg()
APIs to handle incoming connections.
Related information
DosSetRelMaxFH()
Socket programming 85
Examples: Socket application designs
These example programs illustrate the more advanced socket concepts. You can use these example
programs to create your own applications that complete a similar task.
With these examples, there are graphics and a listing of calls that illustrate the flow of events in each of
these applications. You can use the Xsockets tool interactively, try some of these APIs in these programs,
or you can make specific changes for your particular environment.
Iterative server
In the iterative server example, a single server job handles all incoming connections and all data flows
with the client jobs. When the accept() API is completed, the server handles the entire transaction. This
is the easiest server to develop, but it does have a few problems. While the server is handling the request
from a given client, additional clients can be trying to get to the server. These requests fill the listen()
backlog and some of the them are rejected eventually.
Concurrent server
In the concurrent server designs, the system uses multiple jobs and threads to handle the incoming
connection requests. With a concurrent server there are typically multiple clients that connect to the
server at the same time.
For multiple concurrent clients in a network, it is recommended that you use the asynchronous I/O socket
APIs. These APIs provide the best performance in networks that have multiple concurrent clients.
• spawn() server and spawn() worker
The spawn() API is used to create a new job to handle each incoming request. After spawn() is
completed, the server can wait on the accept() API for the next incoming connection to be received.
The only problem with this server design is the performance overhead of creating a new job each time a
connection is received. You can avoid the performance overhead of the spawn() server example by
using prestarted jobs. Instead of creating a new job each time a connection is received, the incoming
connection is given to a job that is already active. All of the remaining examples in this topic use
prestarted jobs.
• sendmsg() server and recvmsg() worker
The sendmsg() and recvmsg() APIs are used to handle incoming connections. The server prestarts
all of the worker jobs when the server job first starts.
• Multiple accept() servers and multiple accept() workers
For the previous APIs, the worker job does not get involved until after the server receives the incoming
connection request. When the multiple accept() APIs are used, each of the worker jobs can be turned
into an iterative server. The server job still calls the socket(), bind(), and listen() APIs. When the
listen() call is completed, the server creates each of the worker jobs and gives a listening socket to
each one of them. All of the worker jobs then call the accept() API. When a client tries to connect to
the server, only one accept() call is completed, and that worker handles the connection.
Related concepts
Asynchronous I/O
Socket programming 87
Flow of socket events: Iterative server
The following sequence of the socket calls provides a description of the graphic. It also describes the
relationship between the server and worker applications. Each set of flows contains links to usage notes
on specific APIs. If you need more details on the use of a particular API, you can use these links. The
following sequence shows the API calls for the iterative server application:
1. The socket() API returns a socket descriptor, which represents an endpoint. The statement also
identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) is
used for this socket.
2. After the socket descriptor is created, the bind() API gets a unique name for the socket.
3. The listen() allows the server to accept incoming client connections.
4. The server uses the accept() API to accept an incoming connection request. The accept() call
blocks indefinitely, waiting for the incoming connection to arrive.
5. The recv() API receives data from the client application.
6. The send() API echoes the data back to the client.
7. The close() API closes any open socket descriptors.
/**************************************************************************/
/* Application creates an iterative server design */
/**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.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_INET6 stream socket to receive */
/* incoming connections on */
/*************************************************/
listen_sd = socket(AF_INET6, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}
/*************************************************/
/* Allow socket descriptor to be reuseable */
/*************************************************/
rc = setsockopt(listen_sd,
SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on));
if (rc < 0)
{
perror("setsockopt() failed");
close(listen_sd);
exit(-1);
}
/*************************************************/
/* Bind the socket */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
addr.sin6_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 back log */
/*************************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}
Socket programming 89
/*************************************************/
/* 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("Interation: %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 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 down the incoming connection */
/**********************************************/
close(accept_sd);
}
/*************************************************/
/* Close down the listen socket */
/*************************************************/
close(listen_sd);
}
Related reference
Example: Generic client
This example contains the code for a common client job. The client job does a socket(), connect(),
send(), recv(), and close() operation.
Related information
recv()--Receive Data API
bind()--Set Local Address for Socket API
socket()--Create Socket API
listen()--Invite Incoming Connections Requests API
accept()--Wait for Connection Request and Make Connection API
Flow of socket events: Server that uses spawn() to accept and process requests
The following sequence of the socket calls provides a description of the graphic. It also describes the
relationship between the server and worker examples. Each set of flows contains links to usage notes on
Socket programming 91
specific APIs. If you need more details about the use of a particular API, you can use these links. The first
example uses the following socket calls to create a child process with the spawn() API call:
1. The socket() API returns a socket descriptor, which represents an endpoint. The statement also
identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) is
used for this socket.
2. After the socket descriptor is created, the bind() API gets a unique name for the socket.
3. The listen() allows the server to accept incoming client connections.
4. The server uses the accept() API to accept an incoming connection request. The accept() call
blocks indefinitely, waiting for the incoming connection to arrive.
5. The spawn() API initializes the parameters for a work job to handle incoming requests. In this
example, the socket descriptor for the new connection is mapped over to descriptor 0 in the child
program.
6. In this example, the first close() API closes the listening socket descriptor. The second close()
call ends the accepted socket.
/**************************************************************************/
/* Application creates an child process using spawn(). */
/**************************************************************************/
#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_INET6 stream socket to receive */
/* incoming connections on */
/*************************************************/
listen_sd = socket(AF_INET6, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}
/*************************************************/
/* Allow socket descriptor to be reuseable */
/*************************************************/
rc = setsockopt(listen_sd,
SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on));
if (rc < 0)
{
perror("setsockopt() failed");
close(listen_sd);
exit(-1);
}
/*************************************************/
/* Bind the socket */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(SERVER_PORT);
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
rc = bind(listen_sd,
(struct sockaddr *)&addr, sizeof(addr));
if (rc < 0)
{
perror("bind() failed");
close(listen_sd);
exit(-1);
}
/*************************************************/
/* Set the listen back log */
/*************************************************/
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("Interation: %d\n", i+1);
Socket programming 93
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");
/**********************************************/
/* Initialize the spawn parameters */
/* */
/* The socket descriptor for the new */
/* connection is mapped over to descriptor 0 */
/* in the child program. */
/**********************************************/
memset(&inherit, 0, sizeof(inherit));
spawn_argv[0] = NULL;
spawn_envp[0] = NULL;
spawn_fdmap[0] = accept_sd;
/**********************************************/
/* Create the worker job */
/**********************************************/
printf(" creating worker job\n");
pid = spawn("/QSYS.LIB/QGPL.LIB/WRKR1.PGM",
1, spawn_fdmap, &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 down the incoming connection since */
/* it has been given to a worker to handle */
/**********************************************/
close(accept_sd);
}
/*************************************************/
/* Close down the listen socket */
/*************************************************/
close(listen_sd);
}
Related reference
Example: Enabling the worker job to receive a data buffer
This example contains the code that enables the worker job to receive a data buffer from the client job
and echo it back.
/**************************************************************************/
/* Worker job that receives and echoes back a data buffer to a client */
/**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
/*************************************************/
/* 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 down the incoming connection */
/*************************************************/
close(sockfd);
Related reference
Example: Creating a server that uses spawn()
This example shows how to use the spawn() API to create a child process that inherits the socket
descriptor from the parent.
Socket programming 95
Flow of socket events: Server that uses sendmsg() and recvmsg() APIs
The following sequence of the socket calls provides a description of the graphic. It also describes the
relationship between the server and worker examples. The first example uses the following socket calls
to create a child process with the sendmsg() and recvmsg() API calls:
1. The socket() API returns a socket descriptor, which represents an endpoint. The statement also
identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) is
used for this socket.
2. After the socket descriptor is created, the bind() API gets a unique name for the socket.
3. The listen() allows the server to accept incoming client connections.
4. The socketpair() API creates a pair of UNIX datagram sockets. A server can use the
socketpair() API to create a pair of AF_UNIX sockets.
5. The spawn() API initializes the parameters for a work job to handle incoming requests. In this
example, the child job created inherits the socket descriptor that was created by the socketpair().
/**************************************************************************/
/* Server example that uses sendmsg() to create worker jobs */
/**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
Socket programming 97
#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_INET6 stream socket to receive */
/* incoming connections on */
/*************************************************/
listen_sd = socket(AF_INET6, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}
/*************************************************/
/* Allow socket descriptor to be reuseable */
/*************************************************/
rc = setsockopt(listen_sd,
SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on));
if (rc < 0)
{
perror("setsockopt() failed");
close(listen_sd);
exit(-1);
}
/*************************************************/
/* Bind the socket */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
addr.sin6_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 back log */
/*************************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}
/*************************************************/
/* Create a pair of UNIX datagram sockets */
/*************************************************/
rc = socketpair(AF_UNIX, SOCK_DGRAM, 0, pair_sd);
/*************************************************/
/* Initialize parms before entering for loop */
/* */
/* The worker socket descriptor is mapped to */
/* descriptor 0 in the child program. */
/*************************************************/
memset(&inherit, 0, sizeof(inherit));
spawn_argv[0] = NULL;
spawn_envp[0] = NULL;
spawn_fdmap[0] = worker_sd;
/*************************************************/
/* 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, spawn_fdmap, &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 down 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++)
{
/**********************************************/
/* Wait for an incoming connection */
/**********************************************/
printf("Interation: %d\n", i+1);
printf(" waiting on accept()\n");
accept_sd = accept(listen_sd, NULL, NULL);
if (accept_sd < 0)
{
perror("accept() failed");
close(listen_sd);
close(server_sd);
exit(-1);
}
printf(" accept completed successfully\n");
/**********************************************/
/* Initialize message header structure */
/**********************************************/
memset(&msg, 0, sizeof(msg));
/**********************************************/
/* We are not sending any data so we 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. */
/**********************************************/
Socket programming 99
/**********************************************/
/* 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: We do not know 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 down the incoming connection since */
/* it has been given to a worker to handle */
/**********************************************/
close(accept_sd);
}
/*************************************************/
/* Close down the server and listen sockets */
/*************************************************/
close(server_sd);
close(listen_sd);
}
Related reference
Example: Generic client
This example contains the code for a common client job. The client job does a socket(), connect(),
send(), recv(), and close() operation.
/**************************************************************************/
/* Worker job that uses the recvmsg to process client requests */
/**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
/*************************************************/
/* One of the socket descriptors that was */
/* returned by socketpair(), is passed to this */
/* worker job as descriptor 0. */
/*************************************************/
worker_sd = 0;
/*************************************************/
/* Initialize message header structure */
/*************************************************/
/*************************************************/
/* 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 we */
/* 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);
}
/*************************************************/
/* Receive a message from the client */
/*************************************************/
printf("Wait for client to send us a message\n");
rc = recv(pass_sd, buffer, sizeof(buffer), 0);
if (rc <= 0)
{
perror("recv() failed");
close(worker_sd);
close(pass_sd);
exit(-1);
}
printf("<%s>\n", buffer);
/*************************************************/
/* Echo the data back to the client */
/*************************************************/
printf("Echo it back\n");
len = rc;
rc = send(pass_sd, buffer, len, 0);
if (rc <= 0)
{
perror("send() failed");
close(worker_sd);
close(pass_sd);
exit(-1);
}
/*************************************************/
/* Close down the descriptors */
/*************************************************/
close(worker_sd);
close(pass_sd);
}
Flow of socket events: Server that creates a pool of multiple accept() worker jobs
The following sequence of the socket calls provides a description of the figure. It also describes the
relationship between the server and worker examples. Each set of flows contains links to usage notes on
specific APIs. If you need more details about the use of a particular API, you can use these links. The first
example uses the following socket calls to create a child process:
1. The socket() API returns a socket descriptor, which represents an endpoint. The statement also
identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) is
used for this socket.
2. After the socket descriptor is created, the bind() API gets a unique name for the socket.
3. The listen() API allows the server to accept incoming client connections.
/*****************************************************************************/
/* Server example creates a pool of worker jobs with multiple accept() calls */
/*****************************************************************************/
#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_INET6 stream socket to receive */
/*************************************************/
/* Allow socket descriptor to be reuseable */
/*************************************************/
rc = setsockopt(listen_sd,
SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on));
if (rc < 0)
{
perror("setsockopt() failed");
close(listen_sd);
exit(-1);
}
/*************************************************/
/* Bind the socket */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
addr.sin6_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 back log */
/*************************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}
/*************************************************/
/* Initialize parameters before entering for loop */
/* */
/* The listen socket descriptor is mapped to */
/* descriptor 0 in the child program. */
/*************************************************/
memset(&inherit, 0, sizeof(inherit));
spawn_argv[0] = NULL;
spawn_envp[0] = NULL;
spawn_fdmap[0] = listen_sd;
/*************************************************/
/* 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",
1, spawn_fdmap, &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");
Related reference
Example: Generic client
This example contains the code for a common client job. The client job does a socket(), connect(),
send(), recv(), and close() operation.
/**************************************************************************/
/* Worker job uses multiple accept() to handle incoming client connections*/
/**************************************************************************/
#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 = 0;
/*************************************************/
/* 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);
/*************************************************/
/* 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);
}
/**************************************************************************/
/* Generic client example is used with connection-oriented server designs */
/**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
/*************************************************/
/* Create an AF_INET6 stream socket */
/*************************************************/
/*************************************************/
/* Initialize the socket address structure */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
addr.sin6_port = htons(SERVER_PORT);
/*************************************************/
/* Connect to the server */
/*************************************************/
rc = connect(sockfd,
(struct sockaddr *)&addr,
sizeof(struct sockaddr_in6));
if (rc < 0)
{
perror("connect");
close(sockfd);
exit(-1);
}
printf("Connect completed.\n");
/*************************************************/
/* Enter data buffer that is to be sent */
/*************************************************/
printf("Enter message to be sent:\n");
gets(send_buf);
/*************************************************/
/* Send data buffer to the worker job */
/*************************************************/
len = send(sockfd, send_buf, strlen(send_buf) + 1, 0);
if (len != strlen(send_buf) + 1)
{
perror("send");
close(sockfd);
exit(-1);
}
printf("%d bytes sent\n", len);
/*************************************************/
/* Receive data buffer from the worker job */
/*************************************************/
len = recv(sockfd, recv_buf, sizeof(recv_buf), 0);
if (len != strlen(send_buf) + 1)
{
perror("recv");
close(sockfd);
exit(-1);
}
printf("%d bytes received\n", len);
/*************************************************/
/* Close down the socket */
/*************************************************/
close(sockfd);
}
Related reference
Example: IPv4 or IPv6 client
This sample program can be used with the server application that accepts requests from either IPv4 or
IPv6 clients.
Examples: Connection-oriented designs
You can design a connection-oriented socket server on the system in a number of ways. These example
programs can be used to create your own connection-oriented designs.
Example: Writing an iterative server program
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#define _MULTI_THREADED
#include "pthread.h"
#include "qsoasync.h"
#define BufferLength 80
#define Failure 0
#define Success 1
#define SERVPORT 12345
/********************************************************************/
/* */
/* Function Name: main */
/* */
/* Descriptive Name: Master thread will establish a client */
/* connection and hand processing responsibility */
/* to a worker thread. */
/* Note: Due to the thread attribute of this program, spawn() must */
/* be used to invoke. */
/********************************************************************/
int main()
{
int listen_sd, client_sd, rc;
int on = 1, ioCompPort;
pthread_t thr;
void *status;
char buffer[BufferLength];
struct sockaddr_in6 serveraddr;
Qso_OverlappedIO_t ioStruct;
/*********************************************/
/* Create an I/O completion port for this */
/* process. */
/*********************************************/
if ((ioCompPort = QsoCreateIOCompletionPort()) < 0)
{
perror("QsoCreateIOCompletionPort() failed");
exit(-1);
}
/*********************************************/
/* Create a worker thread */
/* to process all client requests. The */
/* worker thread will wait for client */
/* requests to arrive on the I/O completion */
/* port just created. */
/*********************************************/
rc = pthread_create(&thr, NULL, workerThread,
&ioCompPort);
if (rc < 0)
{
perror("pthread_create() failed");
QsoDestroyIOCompletionPort(ioCompPort);
close(listen_sd);
exit(-1);
}
/*********************************************/
/* Create an AF_INET6 stream socket to */
/*********************************************/
/* Allow socket descriptor to be reuseable */
/*********************************************/
if ((rc = setsockopt(listen_sd, SOL_SOCKET,
SO_REUSEADDR,
(char *)&on,
sizeof(on))) < 0)
{
perror("setsockopt() failed");
QsoDestroyIOCompletionPort(ioCompPort);
close(listen_sd);
exit(-1);
}
/*********************************************/
/* bind the socket */
/*********************************************/
memset(&serveraddr, 0x00, sizeof(struct sockaddr_in6));
serveraddr.sin6_family = AF_INET6;
serveraddr.sin6_port = htons(SERVPORT);
memcpy(&serveraddr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
if ((rc = bind(listen_sd,
(struct sockaddr *)&serveraddr,
sizeof(serveraddr))) < 0)
{
perror("bind() failed");
QsoDestroyIOCompletionPort(ioCompPort);
close(listen_sd);
exit(-1);
}
/*********************************************/
/* Set listen backlog */
/*********************************************/
if ((rc = listen(listen_sd, 10)) < 0)
{
perror("listen() failed");
QsoDestroyIOCompletionPort(ioCompPort);
close(listen_sd);
exit(-1);
}
/*********************************************/
/* accept an incoming client connection. */
/*********************************************/
if ((client_sd = accept(listen_sd, (struct sockaddr *)NULL,
NULL)) < 0)
{
perror("accept() failed");
QsoDestroyIOCompletionPort(ioCompPort);
close(listen_sd);
exit(-1);
}
/*********************************************/
/* Issue QsoStartRecv() to receive client */
/* request. */
/* Note: */
/* postFlag == on denoting request should */
/* posted to the I/O */
/* completion port, even if */
/* if request is immediately */
/* available. Worker thread */
/* will process client */
/* request. */
/*********************************************/
/*********************************************/
/* initialize Qso_OverlappedIO_t structure - */
/* reserved fields must be hex 00's. */
ioStruct.buffer = buffer;
ioStruct.bufferLength = sizeof(buffer);
/*********************************************/
/* Store the client descriptor in the */
/* Qso_OverlappedIO_t descriptorHandle field.*/
/* This area is used to house information */
/* defining the state of the client */
/* connection. Field descriptorHandle is */
/* defined as a (void *) to allow the server */
/* to address more extensive client */
/* connection state if needed. */
/*********************************************/
*((int*)&ioStruct.descriptorHandle) = client_sd;
ioStruct.postFlag = 1;
ioStruct.fillBuffer = 0;
/*********************************************/
/* Wait for worker thread to finish */
/* processing client connection. */
/*********************************************/
rc = pthread_join(thr, &status);
QsoDestroyIOCompletionPort(ioCompPort);
if ( rc == 0 && (rc = __INT(status)) == Success)
{
printf("Success.\n");
exit(0);
}
else
{
perror("pthread_join() reported failure");
exit(-1);
}
}
/* end workerThread */
/********************************************************************/
/* */
/* Function Name: workerThread */
/* */
/* Descriptive Name: Process client connection. */
/********************************************************************/
void *workerThread(void *arg)
{
struct timeval waitTime;
int ioCompPort, clientfd;
Qso_OverlappedIO_t ioStruct;
int rc, tID;
pthread_t thr;
pthread_id_np_t t_id;
t_id = pthread_getthreadid_np();
tID = t_id.intId.lo;
/*********************************************/
/* I/O completion port is passed to this */
/* routine. */
/*********************************************/
ioCompPort = *(int *)arg;
/*********************************************/
/* Wait on the supplied I/O completion port */
return __VOID(Failure);
}
/*********************************************/
/* Obtain the socket descriptor associated */
/* with the client connection. */
/*********************************************/
clientfd = *((int *) &ioStruct.descriptorHandle);
/*********************************************/
/* Echo the data back to the client. */
/* Note: postFlag == 0. If write completes */
/* immediate then indication will be */
/* returned, otherwise once the */
/* write is performed the I/O Completion */
/* port will be posted. */
/*********************************************/
ioStruct.postFlag = 0;
ioStruct.bufferLength = ioStruct.returnValue;
rc = QsoStartSend(clientfd, ioCompPort, &ioStruct);
if (rc == 0)
/*********************************************/
/* Operation complete - data has been sent. */
/*********************************************/
;
else
{
/*********************************************/
/* Two possibilities */
/* rc == -1 */
/* Error on function call */
/* rc == 1 */
/* Write cannot be immediately */
/* performed. Once complete, the I/O */
/* completion port will be posted. */
/*********************************************/
if (rc == -1)
{
printf("QsoStartSend() failed.\n");
perror("QsoStartSend() failed");
close(clientfd);
return __VOID(Failure);
}
/*********************************************/
/* Wait for operation to complete. */
/*********************************************/
rc = QsoWaitForIOCompletion(ioCompPort, &ioStruct, &waitTime);
if (rc == 1 && ioStruct.returnValue != -1)
/*********************************************/
/* Send successful. */
/*********************************************/
;
else
{
printf("QsoWaitForIOCompletion() or QsoStartSend() failed.\n");
if(rc != 1)
perror("QsoWaitForIOCompletion() failed");
if(ioStruct.returnValue == -1)
printf("QsoStartRecv() failed - %s\n",
strerror(ioStruct.errnoValue));
return __VOID(Failure);
Related concepts
Asynchronous I/O
Asynchronous I/O APIs provide a method for threaded client/server models to perform highly concurrent
and memory-efficient I/O.
Related reference
Socket application design recommendations
Before working with a socket application, assess the functional requirements, goals, and needs of the
socket application. Also, consider the performance requirements and the system resource impacts of the
application.
Examples: Connection-oriented designs
You can design a connection-oriented socket server on the system in a number of ways. These example
programs can be used to create your own connection-oriented designs.
Example: Generic client
This example contains the code for a common client job. The client job does a socket(), connect(),
send(), recv(), and close() operation.
Example: Using signals with blocking socket APIs
When a process or an application becomes blocked, signals allow you to be notified. They also provide a
time limit for blocking processes.
Related information
QsoCreateIOCompletionPort()--Create I/O Completion Port API
pthread_create
QsoWaitForIOCompletion()--Wait for I/O Operation API
QsoStartAccept()--Start asynchronous accept operation API
QsoStartSend()--Start Asynchronous Send Operation API
QsoDestroyIOCompletionPort()--Destroy I/O Completion Port API
The following sequence of the socket calls provides a description of the graphic. It also describes the
relationship between the server and client examples.
1. The QsoCreateIOCompletionPort() API creates an I/O completion port.
2. The pthread_create API creates a worker thread to receive data and to echo it back to the client.
The worker thread waits for client requests to arrive on the I/O completion port just created.
3. A call to gsk_environment_open() to obtain a handle to an SSL environment.
4. One or more calls to gsk_attribute_set_xxxxx() to set attributes of the SSL environment. At a
minimum, either a call to gsk_attribute_set_buffer() to set the
GSK_OS400_APPLICATION_ID value or to set the GSK_KEYRING_FILE value. Only one of these
should be set. It is preferred that you use the GSK_OS400_APPLICATION_ID value. Also ensure you
set the type of application (client or server), GSK_SESSION_TYPE, using
gsk_attribute_set_enum().
5. A call to gsk_environment_init() to initialize this environment for SSL processing and to
establish the SSL security information for all SSL sessions that run using this environment.
6. The socket API creates a socket descriptor. The server then issues the standard set of socket calls:
bind(), listen(), and accept() to enable a server to accept incoming connection requests.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <gskssl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define _MULTI_THREADED
#include "pthread.h"
#include "qsoasync.h"
#define Failure 0
#define Success 1
#define TRUE 1
#define FALSE 0
/*********************************************/
/* Issue all of the command in a do/while */
/* loop so that clean up can happen at end */
/*********************************************/
do
{
/*********************************************/
/* Create an I/O completion port for this */
/* process. */
/*********************************************/
if ((ioCompPort = QsoCreateIOCompletionPort()) < 0)
{
perror("QsoCreateIOCompletionPort() failed");
break;
}
/*********************************************/
/* Create a worker thread */
/* to process all client requests. The */
/* worker thread will wait for client */
/* requests to arrive on the I/O completion */
/* port just created. */
/*********************************************/
rc = pthread_create(&thr, NULL, workerThread, &ioCompPort);
if (rc < 0)
{
perror("pthread_create() failed");
break;
}
/*********************************************/
/* Issue gsk_secure_soc_startRecv() to */
/* receive client request. */
/* Note: */
/* postFlag == on denoting request should */
/* posted to the I/O completion port, even */
/* if request is immediately available. */
/* Worker thread will process client request.*/
/*********************************************/
/*********************************************/
/* initialize Qso_OverlappedIO_t structure - */
/* reserved fields must be hex 00's. */
/*********************************************/
memset(&ioStruct, '\0', sizeof(ioStruct));
memset((char *) buff, 0, sizeof(buff));
ioStruct.buffer = buff;
ioStruct.bufferLength = sizeof(buff);
/*********************************************/
/* Store the session handle in the */
/* Qso_OverlappedIO_t descriptorHandle field.*/
/* This area is used to house information */
/* defining the state of the client */
/* connection. Field descriptorHandle is */
/* defined as a (void *) to allow the server */
/* to address more extensive client */
/* connection state if needed. */
/*********************************************/
ioStruct.descriptorHandle = my_session_handle;
ioStruct.postFlag = 1;
ioStruct.fillBuffer = 0;
rc = gsk_secure_soc_startRecv(my_session_handle,
ioCompPort,
&ioStruct);
if (rc != GSK_AS400_ASYNCHRONOUS_RECV)
{
printf("gsk_secure_soc_startRecv() rc = %d & errno = %d.\n",rc,errno);
printf("rc of %d means %s\n", rc, gsk_strerror(rc));
break;
}
/*********************************************/
/* This is where the server can loop back */
/* to accept a new connection. */
/*********************************************/
/*********************************************/
/* Wait for worker thread to finish */
/* processing client connection. */
/*********************************************/
rc = pthread_join(thr, &status);
if (successFlag)
exit(0);
else
exit(-1);
}
/********************************************************************/
/* Function Name: workerThread */
/* */
/* Descriptive Name: Process client connection. */
/* */
/* Note: To make the sample more straight forward the main routine */
/* handles all of the clean up although this function can */
/* be made responsible for the clientfd and session_handle. */
/********************************************************************/
void *workerThread(void *arg)
{
struct timeval waitTime;
int ioCompPort = -1, clientfd = -1;
Qso_OverlappedIO_t ioStruct;
int rc, tID;
int amtWritten;
gsk_handle client_session_handle = NULL;
pthread_t thr;
pthread_id_np_t t_id;
t_id = pthread_getthreadid_np();
tID = t_id.intId.lo;
/*********************************************/
/* I/O completion port is passed to this */
/* routine. */
/*********************************************/
ioCompPort = *(int *)arg;
/*********************************************/
/* Wait on the supplied I/O completion port */
/* for a client request. */
/*********************************************/
waitTime.tv_sec = 500;
waitTime.tv_usec = 0;
rc = QsoWaitForIOCompletion(ioCompPort, &ioStruct, &waitTime);
if ((rc == 1) &&
(ioStruct.returnValue == GSK_OK) &&
(ioStruct.operationCompleted == GSKSECURESOCSTARTRECV))
/*********************************************/
/* Client request has been received. */
/*********************************************/
;
else
{
perror("QsoWaitForIOCompletion()/gsk_secure_soc_startRecv() failed");
printf("ioStruct.returnValue = %d.\n", ioStruct.returnValue);
return __VOID(Failure);
}
/*********************************************/
/* Obtain the session handle associated */
/* with the client connection. */
/*********************************************/
client_session_handle = ioStruct.descriptorHandle;
return __VOID(Success);
} /* end workerThread */
Related concepts
Global Security Kit (GSKit) APIs
Global Security Kit (GSKit) is a set of programmable interfaces that allow an application to be SSL
enabled.
Related reference
Example: Establishing a secure client with Global Security Kit APIs
This example demonstrates how to establish a client using the Global Security Kit (GSKit) APIs.
Example: GSKit secure server with asynchronous handshake
The gsk_secure_soc_startInit() API allows you to create secure server applications that can
handle requests asynchronously.
Related information
QsoCreateIOCompletionPort()--Create I/O Completion Port API
pthread_create
QsoWaitForIOCompletion()--Wait for I/O Operation API
QsoDestroyIOCompletionPort()--Destroy I/O Completion Port API
bind()--Set Local Address for Socket API
socket()--Create Socket API
listen()--Invite Incoming Connections Requests API
close()--Close File or Socket Descriptor API
accept()--Wait for Connection Request and Make Connection API
gsk_environment_open()--Get a handle for an SSL environment API
gsk_attribute_set_buffer()--Set character information for a secure session or an SSL environment API
gsk_attribute_set_enum()--Set enumerated information for a secure session or an SSL environment API
gsk_environment_init()--Initialize an SSL environment API
gsk_secure_soc_open()--Get a handle for a secure session API
gsk_attribute_set_numeric_value()--Set numeric information for a secure session or an SSL environment
API
gsk_secure_soc_init()--Negotiate a secure session API
gsk_secure_soc_startRecv()--Start asynchronous receive operation on a secure session API
Socket flow of events: Worker thread that process secure asynchronous requests
1. After the server application creates a worker thread, it waits for the server to send it the incoming
client request to process. The QsoWaitForIOCompletionPort() API waits for the supplied I/O
completion port that was specified by the server. This call waits until the
gsk_secure_soc_startInit() call is completed.
2. As soon as the client request has been received, the gsk_attribute_get_numeric_value() API
gets the socket descriptor associated with the secure session.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <gskssl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define _MULTI_THREADED
#include "pthread.h"
#include "qsoasync.h"
#define Failure 0
#define Success 1
#define TRUE 1
#define FALSE 0
/*********************************************/
/* Issue all of the command in a do/while */
/* loop so that clean up can happen at end */
/*********************************************/
do
{
/*********************************************/
/* Create an I/O completion port for this */
/* process. */
/*********************************************/
if ((ioCompPort = QsoCreateIOCompletionPort()) < 0)
{
perror("QsoCreateIOCompletionPort() failed");
break;
}
/*********************************************/
/* Create a worker thread */
/* to process all client requests. The */
/* worker thread will wait for client */
/* requests to arrive on the I/O completion */
/* port just created. */
/*********************************************/
/* Issue gsk_secure_soc_startInit() to */
/* process SSL Handshake flow asynchronously */
/*********************************************/
/*********************************************/
/* initialize Qso_OverlappedIO_t structure - */
/* reserved fields must be hex 00's. */
/*********************************************/
memset(&ioStruct, '\0', sizeof(ioStruct));
/*********************************************/
/* Store the session handle in the */
/* Qso_OverlappedIO_t descriptorHandle field.*/
/* This area is used to house information */
/* defining the state of the client */
/* connection. Field descriptorHandle is */
/* defined as a (void *) to allow the server */
/* to address more extensive client */
/* connection state if needed. */
/*********************************************/
ioStruct.descriptorHandle = my_session_handle;
/*********************************************/
/* This is where the server can loop back */
/* to accept a new connection. */
/*********************************************/
/*********************************************/
/* Wait for worker thread to finish */
/* processing client connection. */
/*********************************************/
rc = pthread_join(thr, &status);
if (successFlag)
exit(0);
exit(-1);
}
/********************************************************************/
/* Function Name: workerThread */
/* */
/* Descriptive Name: Process client connection. */
/* */
/* Note: To make the sample more straight forward the main routine */
/* handles all of the clean up although this function can */
/* be made responsible for the clientfd and session_handle. */
/********************************************************************/
void *workerThread(void *arg)
{
struct timeval waitTime;
int ioCompPort, clientfd;
Qso_OverlappedIO_t ioStruct;
int rc, tID;
int amtWritten, amtRead;
char buff[1024];
gsk_handle client_session_handle;
pthread_t thr;
pthread_id_np_t t_id;
t_id = pthread_getthreadid_np();
tID = t_id.intId.lo;
/*********************************************/
/* I/O completion port is passed to this */
/* routine. */
/*********************************************/
ioCompPort = *(int *)arg;
/*********************************************/
/* Wait on the supplied I/O completion port */
/* for the SSL handshake to complete. */
/*********************************************/
waitTime.tv_sec = 500;
waitTime.tv_usec = 0;
sleep(4);
printf("QsoWaitForIOCompletion()\n");
rc = QsoWaitForIOCompletion(ioCompPort, &ioStruct, &waitTime);
if ((rc == 1) &&
(ioStruct.returnValue == GSK_OK) &&
(ioStruct.operationCompleted == GSKSECURESOCSTARTINIT))
/*********************************************/
/* SSL Handshake has completed. */
/*********************************************/
;
else
{
printf("QsoWaitForIOCompletion()/gsk_secure_soc_startInit() failed.\n");
printf("rc == %d, returnValue - %d, operationCompleted = %d\n",
rc, ioStruct.returnValue, ioStruct.operationCompleted);
perror("QsoWaitForIOCompletion()/gsk_secure_soc_startInit() failed");
return __VOID(Failure);
}
/*********************************************/
/* Obtain the session handle associated */
/* with the client connection. */
/*********************************************/
client_session_handle = ioStruct.descriptorHandle;
if (rc != GSK_OK)
{
printf("gsk_secure_soc_read() rc = %d and errno = %d.\n",rc,errno);
printf("rc of %d means %s\n", rc, gsk_strerror(rc));
return;
}
return __VOID(Success);
}
/* end workerThread */
Related concepts
Global Security Kit (GSKit) APIs
Global Security Kit (GSKit) is a set of programmable interfaces that allow an application to be SSL
enabled.
Related reference
Example: Establishing a secure client with Global Security Kit APIs
This example demonstrates how to establish a client using the Global Security Kit (GSKit) APIs.
Example: GSKit secure server with asynchronous data receive
This example demonstrates how to establish a secure server using Global Security Kit (GSKit) APIs.
Related information
QsoCreateIOCompletionPort()--Create I/O Completion Port API
pthread_create
QsoWaitForIOCompletion()--Wait for I/O Operation API
QsoDestroyIOCompletionPort()--Destroy I/O Completion Port API
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <gskssl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define TRUE 1
#define FALSE 0
void main(void)
{
gsk_handle my_env_handle=NULL; /* secure environment handle */
gsk_handle my_session_handle=NULL; /* secure session handle */
/*********************************************/
/* Issue all of the command in a do/while */
/* loop so that cleanup can happen at end */
/*********************************************/
do
{
/* open a gsk environment */
rc = errno = 0;
rc = gsk_environment_open(&my_env_handle);
if (rc != GSK_OK)
{
printf("gsk_environment_open() failed with rc = %d and errno = %d.\n",
rc,errno);
printf("rc of %d means %s\n", rc, gsk_strerror(rc));
break;
}
if (rc != GSK_OK)
{
printf("gsk_secure_soc_read() rc = %d and errno = %d.\n",rc,errno);
printf("rc of %d means %s\n", rc, gsk_strerror(rc));
break;
}
} while(FALSE);
return;
}
Related concepts
Global Security Kit (GSKit) APIs
Global Security Kit (GSKit) is a set of programmable interfaces that allow an application to be SSL
enabled.
Related reference
Example: GSKit secure server with asynchronous data receive
This example demonstrates how to establish a secure server using Global Security Kit (GSKit) APIs.
Example: GSKit secure server with asynchronous handshake
/********************************************************/
/* 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
/********************************************************/
/* Pass one parameter that is the IP address in */
/* dotted decimal notation. The host name will be */
/* displayed if found; otherwise, a message states */
/* host not found. */
/********************************************************/
int main(int argc, char *argv[])
{
int rc;
struct in_addr internet_address;
struct hostent hst_ent;
struct hostent_data hst_ent_data;
char dotted_decimal_address [16];
char host_name[MAXHOSTNAMELEN];
/**********************************************************/
/* Verify correct number of arguments have been passed */
/**********************************************************/
if (argc != NUMPARMS)
{
printf("Wrong number of parms passed\n");
exit(-1);
}
/**********************************************************/
/**********************************************************/
/* 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 IP 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) */
/**********************************************************/
rc=gethostbyaddr_r((char *) &internet_address,
sizeof(struct in_addr), AF_INET,
&hst_ent, &hst_ent_data);
if (rc== -1)
{
printf("Host name not found\n");
exit(-1);
}
else
{
/*****************************************************/
/* Copy the host name to an output buffer */
/*****************************************************/
(void) memcpy((void *) host_name,
/****************************************************/
/* You must address all the results through the */
/* hostent structure hst_ent. */
/* NOTE: Hostent_data structure hst_ent_data is just */
/* a data repository that is used to support the */
/* hostent structure. Applications should consider */
/* hostent_data a storage area to put host level data */
/* that the application does not need to access. */
/****************************************************/
(void *) hst_ent.h_name,
MAXHOSTNAMELEN);
/*****************************************************/
/* Print the host name */
/*****************************************************/
printf("The host name is %s\n", host_name);
}
exit(0);
}
Related concepts
Thread safety
A function is considered threadsafe if you can start it simultaneously in multiple threads within the same
process. A function is threadsafe only if all the functions it calls are also threadsafe. Socket APIs consist
of system and network functions, which are both threadsafe.
Related reference
Socket network functions
Socket flow of events: Server that uses nonblocking I/O and select()
The following calls are used in the example:
1. The socket() API returns a socket descriptor, which represents an endpoint. The statement also
identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) is
used for this socket.
2. The ioctl() API allows the local address to be reused when the server is restarted before the
required wait time expires. In this example, it sets the socket to be nonblocking. All of the sockets for
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>
#define TRUE 1
#define FALSE 0
/*************************************************************/
/* Create an AF_INET6 stream socket to receive incoming */
/* connections on */
/*************************************************************/
listen_sd = socket(AF_INET6, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}
/*************************************************************/
/* Allow socket descriptor to be reuseable */
/*************************************************************/
rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR,
/*************************************************************/
/* Set socket to be nonblocking. All of the sockets for */
/* the incoming connections will also be nonblocking since */
/* they will inherit that state from the listening socket. */
/*************************************************************/
rc = ioctl(listen_sd, FIONBIO, (char *)&on);
if (rc < 0)
{
perror("ioctl() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Bind the socket */
/*************************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
addr.sin6_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 back log */
/*************************************************************/
rc = listen(listen_sd, 32);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Initialize the master fd_set */
/*************************************************************/
FD_ZERO(&master_set);
max_sd = listen_sd;
FD_SET(listen_sd, &master_set);
/*************************************************************/
/* Initialize the timeval struct to 3 minutes. If no */
/* activity after 3 minutes this program will end. */
/*************************************************************/
timeout.tv_sec = 3 * 60;
timeout.tv_usec = 0;
/*************************************************************/
/* Loop waiting for incoming connects or for incoming data */
/* on any of the connected sockets. */
/*************************************************************/
do
{
/**********************************************************/
/* Copy the master fd_set over to the working fd_set. */
/**********************************************************/
memcpy(&working_set, &master_set, sizeof(master_set));
/**********************************************************/
/* Call select() and wait 3 minutes for it to complete. */
/**********************************************************/
printf("Waiting on select()...\n");
rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);
/**********************************************************/
/* Check to see if the select call failed. */
/**********************************************************/
/**********************************************************/
/* Check to see if the 3 minute time out expired. */
/**********************************************************/
if (rc == 0)
{
printf(" select() timed out. End program.\n");
break;
}
/**********************************************************/
/* One or more descriptors are readable. Need to */
/* determine which ones they are. */
/**********************************************************/
desc_ready = rc;
for (i=0; i <= max_sd && desc_ready > 0; ++i)
{
/*******************************************************/
/* Check to see if this descriptor is ready */
/*******************************************************/
if (FD_ISSET(i, &working_set))
{
/****************************************************/
/* A descriptor was found that was readable - one */
/* less has to be looked for. This is being done */
/* so that we can stop looking at the working set */
/* once we have found all of the descriptors that */
/* were ready. */
/****************************************************/
desc_ready -= 1;
/****************************************************/
/* Check to see if this is the listening socket */
/****************************************************/
if (i == listen_sd)
{
printf(" Listening socket is readable\n");
/*************************************************/
/* Accept all incoming connections that are */
/* queued up on the listening socket before we */
/* loop back and call select again. */
/*************************************************/
do
{
/**********************************************/
/* Accept each incoming connection. If */
/* accept fails with EWOULDBLOCK, then we */
/* have accepted all of them. Any other */
/* failure on accept will cause us to end the */
/* server. */
/**********************************************/
new_sd = accept(listen_sd, NULL, NULL);
if (new_sd < 0)
{
if (errno != EWOULDBLOCK)
{
perror(" accept() failed");
end_server = TRUE;
}
break;
}
/**********************************************/
/* Add the new incoming connection to the */
/* master read set */
/**********************************************/
printf(" New incoming connection - %d\n", new_sd);
FD_SET(new_sd, &master_set);
if (new_sd > max_sd)
max_sd = new_sd;
/**********************************************/
/* Loop back up and accept another incoming */
/* connection */
/**********************************************/
} while (new_sd != -1);
}
/**********************************************/
/* Check to see if the connection has been */
/* closed by the client */
/**********************************************/
if (rc == 0)
{
printf(" Connection closed\n");
close_conn = TRUE;
break;
}
/**********************************************/
/* Data was received */
/**********************************************/
len = rc;
printf(" %d bytes received\n", len);
/**********************************************/
/* Echo the data back to the client */
/**********************************************/
rc = send(i, buffer, len, 0);
if (rc < 0)
{
perror(" send() failed");
close_conn = TRUE;
break;
}
} while (TRUE);
/*************************************************/
/* If the close_conn flag was turned on, we need */
/* to clean up this active connection. This */
/* clean up process includes removing the */
/* descriptor from the master set and */
/* determining the new maximum descriptor value */
/* based on the bits that are still turned on in */
/* the master set. */
/*************************************************/
if (close_conn)
{
close(i);
FD_CLR(i, &master_set);
if (i == max_sd)
{
while (FD_ISSET(max_sd, &master_set) == FALSE)
max_sd -= 1;
}
}
} /* End of existing connection is readable */
/*************************************************************/
/* Clean up all of the sockets that are open */
/*************************************************************/
for (i=0; i <= max_sd; ++i)
{
if (FD_ISSET(i, &master_set))
close(i);
}
}
Related concepts
Nonblocking I/O
When an application issues one of the socket input APIs and there is no data to read, the API blocks and
does not return until there is data to read.
I/O multiplexing—select()
Because asynchronous I/O provides a more efficient way to maximize your application resources, it is
recommended that you use asynchronous I/O APIs rather than the select() API. However, your
specific application design might allow select() to be used.
Related reference
Socket application design recommendations
Before working with a socket application, assess the functional requirements, goals, and needs of the
socket application. Also, consider the performance requirements and the system resource impacts of the
application.
Example: Generic client
This example contains the code for a common client job. The client job does a socket(), connect(),
send(), recv(), and close() operation.
Related information
accept()--Wait for Connection Request and Make Connection API
recv()--Receive Data API
ioctl()--Perform I/O Control Request API
send()--Send Data API
listen()--Invite Incoming Connections Requests API
close()--Close File or Socket Descriptor API
socket()--Create Socket API
bind()--Set Local Address for Socket API
select()--Wait for Events on Multiple Sockets API
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>
#define TRUE 1
#define FALSE 0
/*************************************************************/
/* Create an AF_INET6 stream socket to receive incoming */
/* connections on */
/*************************************************************/
listen_sd = socket(AF_INET6, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
/*************************************************************/
/* Allow socket descriptor to be reuseable */
/*************************************************************/
rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on));
if (rc < 0)
{
perror("setsockopt() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Set socket to be nonblocking. All of the sockets for */
/* the incoming connections will also be nonblocking since */
/* they will inherit that state from the listening socket. */
/*************************************************************/
rc = ioctl(listen_sd, FIONBIO, (char *)&on);
if (rc < 0)
{
perror("ioctl() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Bind the socket */
/*************************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
addr.sin6_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 back log */
/*************************************************************/
rc = listen(listen_sd, 32);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Initialize the pollfd structure */
/*************************************************************/
memset(fds, 0 , sizeof(fds));
/*************************************************************/
/* Set up the initial listening socket */
/*************************************************************/
fds[0].fd = listen_sd;
fds[0].events = POLLIN;
/*************************************************************/
/* Initialize the timeout to 3 minutes. If no */
/* activity after 3 minutes this program will end. */
/* timeout value is based on milliseconds. */
/*************************************************************/
timeout = (3 * 60 * 1000);
/*************************************************************/
/* Loop waiting for incoming connects or for incoming data */
/* on any of the connected sockets. */
/*************************************************************/
do
{
/***********************************************************/
/* Call poll() and wait 3 minutes for it to complete. */
/***********************************************************/
printf("Waiting on poll()...\n");
rc = poll(fds, nfds, timeout);
/***********************************************************/
/* Check to see if the 3 minute time out expired. */
/***********************************************************/
if (rc == 0)
{
printf(" poll() timed out. End program.\n");
break;
}
/***********************************************************/
/* One or more descriptors are readable. Need to */
/* determine which ones they are. */
/***********************************************************/
current_size = nfds;
for (i = 0; i < current_size; i++)
{
/*********************************************************/
/* Loop through to find the descriptors that returned */
/* POLLIN and determine whether it's the listening */
/* or the active connection. */
/*********************************************************/
if(fds[i].revents == 0)
continue;
/*********************************************************/
/* If revents is not POLLIN, it's an unexpected result, */
/* log and end the server. */
/*********************************************************/
if(fds[i].revents != POLLIN)
{
printf(" Error! revents = %d\n", fds[i].revents);
end_server = TRUE;
break;
}
if (fds[i].fd == listen_sd)
{
/*******************************************************/
/* Listening descriptor is readable. */
/*******************************************************/
printf(" Listening socket is readable\n");
/*******************************************************/
/* Accept all incoming connections that are */
/* queued up on the listening socket before we */
/* loop back and call poll again. */
/*******************************************************/
do
{
/*****************************************************/
/* Accept each incoming connection. If */
/* accept fails with EWOULDBLOCK, then we */
/* have accepted all of them. Any other */
/* failure on accept will cause us to end the */
/* server. */
/*****************************************************/
new_sd = accept(listen_sd, NULL, NULL);
if (new_sd < 0)
{
if (errno != EWOULDBLOCK)
{
perror(" accept() failed");
end_server = TRUE;
}
break;
}
/*****************************************************/
/* Add the new incoming connection to the */
/* pollfd structure */
/*****************************************************/
/*****************************************************/
/* Loop back up and accept another incoming */
/* connection */
/*****************************************************/
} while (new_sd != -1);
}
/*********************************************************/
/* This is not the listening socket, therefore an */
/* existing connection must be readable */
/*********************************************************/
else
{
printf(" Descriptor %d is readable\n", fds[i].fd);
close_conn = FALSE;
/*******************************************************/
/* Receive all incoming data on this socket */
/* before we loop back and call poll again. */
/*******************************************************/
do
{
/*****************************************************/
/* Receive data on this connection until the */
/* recv fails with EWOULDBLOCK. If any other */
/* failure occurs, we will close the */
/* connection. */
/*****************************************************/
rc = recv(fds[i].fd, buffer, sizeof(buffer), 0);
if (rc < 0)
{
if (errno != EWOULDBLOCK)
{
perror(" recv() failed");
close_conn = TRUE;
}
break;
}
/*****************************************************/
/* Check to see if the connection has been */
/* closed by the client */
/*****************************************************/
if (rc == 0)
{
printf(" Connection closed\n");
close_conn = TRUE;
break;
}
/*****************************************************/
/* Data was received */
/*****************************************************/
len = rc;
printf(" %d bytes received\n", len);
/*****************************************************/
/* Echo the data back to the client */
/*****************************************************/
rc = send(fds[i].fd, buffer, len, 0);
if (rc < 0)
{
perror(" send() failed");
close_conn = TRUE;
break;
}
} while(TRUE);
/*******************************************************/
/* If the close_conn flag was turned on, we need */
/* to clean up this active connection. This */
/* clean up process includes removing the */
/* descriptor. */
/*******************************************************/
if (close_conn)
/***********************************************************/
/* If the compress_array flag was turned on, we need */
/* to squeeze together the array and decrement the number */
/* of file descriptors. We do not need to move back the */
/* events and revents fields because the events will always*/
/* be POLLIN in this case, and revents is output. */
/***********************************************************/
if (compress_array)
{
compress_array = FALSE;
for (i = 0; i < nfds; i++)
{
if (fds[i].fd == -1)
{
for(j = i; j < nfds; j++)
{
fds[j].fd = fds[j+1].fd;
}
i--;
nfds--;
}
}
}
/*************************************************************/
/* Clean up all of the sockets that are open */
/*************************************************************/
for (i = 0; i < nfds; i++)
{
if(fds[i].fd >= 0)
close(fds[i].fd);
}
}
Related information
accept()--Wait for Connection Request and Make Connection API
recv()--Receive Data API
ioctl()--Perform I/O Control Request API
send()--Send Data API
listen()--Invite Incoming Connections Requests API
close()--Close File or Socket Descriptor API
socket()--Create Socket API
bind()--Set Local Address for Socket API
setsockopt()--Set Socket Options API
poll()--Wait for Events on Multiple Descriptors API
/******************************************************************/
/* Example shows how to set alarms for blocking socket APIs */
/******************************************************************/
/******************************************************************/
/* Include files */
/******************************************************************/
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
/******************************************************************/
/******************************************************************/
/* Main program */
/******************************************************************/
int main(int argc, char *argv[])
{
struct sigaction sact;
struct sockaddr_in6 addr;
time_t t;
int sd, rc;
/******************************************************************/
/* Create an AF_INET6, SOCK_STREAM socket */
/******************************************************************/
printf("Create a TCP socket\n");
sd = socket(AF_INET6, SOCK_STREAM, 0);
if (sd == -1)
{
perror(" socket failed");
return(-1);
}
/******************************************************************/
/* Bind the socket. A port number was not specified because */
/* we are not going to ever connect to this socket. */
/******************************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
printf("Bind the socket\n");
rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
if (rc != 0)
{
perror(" bind failed");
close(sd);
return(-2);
}
/******************************************************************/
/* Perform a listen on the socket. */
/******************************************************************/
printf("Set the listen backlog\n");
rc = listen(sd, 5);
if (rc != 0)
{
perror(" listen failed");
close(sd);
return(-3);
}
/******************************************************************/
/* Set up an alarm that will go off in 5 seconds. */
/******************************************************************/
printf("\nSet an alarm to go off in 5 seconds. This alarm will cause the\n");
printf("blocked accept() to return a -1 and an errno value of EINTR.\n\n");
sigemptyset(&sact.sa_mask);
sact.sa_flags = 0;
sact.sa_handler = catcher;
sigaction(SIGALRM, &sact, NULL);
alarm(5);
/******************************************************************/
/* Display the current time when the alarm was set */
/******************************************************************/
time(&t);
printf("Before accept(), time is %s", ctime(&t));
/******************************************************************/
/* Call accept. This call will normally block indefinitely, */
/* but because we have an alarm set, it will only block for */
/* 5 seconds. When the alarm goes off, the accept call will */
/* complete with -1 and an errno value of EINTR. */
/******************************************************************/
errno = 0;
printf(" Wait for an incoming connection to arrive\n");
rc = accept(sd, NULL, NULL);
/******************************************************************/
/* Show what time it was when the alarm went off */
/******************************************************************/
time(&t);
printf("After accept(), time is %s\n", ctime(&t));
close(sd);
return(0);
}
Related concepts
Signals
An application program can request to be notified asynchronously (request that the system send a signal)
when a condition that the application is interested in occurs.
Asynchronous I/O
Asynchronous I/O APIs provide a method for threaded client/server models to perform highly concurrent
and memory-efficient I/O.
Related reference
Berkeley Software Distribution compatibility
Sockets is a Berkeley Software Distribution (BSD) interface.
Example: Using asynchronous I/O
An application creates an I/O completion port using the QsoCreateIOCompletionPort() API. This API
returns a handle that can be used to schedule and wait for completion of asynchronous I/O requests.
Related information
accept()--Wait for Connection Request and Make Connection API
listen()--Invite Incoming Connections Requests API
close()--Close File or Socket Descriptor API
socket()--Create Socket API
bind()--Set Local Address for Socket API
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
/*
* 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.
*/
localInterface.s_addr = inet_addr("9.5.1.1");
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&localInterface,
sizeof(localInterface)) < 0) {
perror("setting local interface");
exit(1);
}
/*
* Send a message to the multicast group specified by the
* groupSock sockaddr structure.
*/
datalen = 10;
if (sendto(sd, databuf, datalen, 0,
(struct sockaddr*)&groupSock,
sizeof(groupSock)) < 0)
{
perror("sending datagram message");
}
}
Related reference
Examples: Using multicasting with AF_INET
#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. */
/* */
/* ------------------------------------------------------------*/
/*
* Create a datagram socket on which to receive.
*/
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("opening datagram socket");
exit(1);
}
/*
* Enable SO_REUSEADDR to allow multiple instances of this
* application to receive copies of the multicast datagrams.
*/
{
int reuse=1;
/*
* 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.
*/
/*
* Read from the socket.
*/
datalen = sizeof(databuf);
if (read(sd, databuf, datalen) < 0) {
perror("reading datagram message");
close(sd);
exit(1);
}
/**************************************************************************/
/* This program updates a DNS using a transaction signature (TSIG) to */
/* sign the update packet. It then queries the DNS to verify success. */
/**************************************************************************/
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <netdb.h>
/**************************************************************************/
/* Declare update records - a zone record, a pre-requisite record, and */
/* 2 update records */
/**************************************************************************/
ns_updrec update_records[] =
{
{
{NULL,&update_records[1]},
{NULL,&update_records[1]},
ns_s_zn, /* a zone record */
"mydomain.ibm.com.",
ns_c_in,
ns_t_soa,
0,
NULL,
0,
0,
NULL,
NULL,
0
},
{
{&update_records[0],&update_records[2]},
{&update_records[0],&update_records[2]},
ns_s_pr, /* pre-req record */
"mypc.mydomain.ibm.com.",
ns_c_in,
ns_t_a,
0,
NULL,
0,
ns_r_nxdomain, /* record must not exist */
NULL,
NULL,
0
},
{
/**************************************************************************/
/* These two structures define a key and secret that must match the one */
/* configured on the DNS : */
/* allow-update { */
/* key my-long-key.; */
/* } */
/* */
/* This must be the binary equivalent of the base64 secret for */
/* the key */
/**************************************************************************/
unsigned char secret[18] =
{
0x6E,0x86,0xDC,0x7A,0xB9,0xE8,0x86,0x8B,0xAA,
0x96,0x89,0xE1,0x91,0xEC,0xB3,0xD7,0x6D,0xF8
};
ns_tsig_key my_key = {
"my-long-key", /* This key must exist on the DNS */
NS_TSIG_ALG_HMAC_MD5,
secret,
sizeof(secret)
};
void main()
{
/***********************************************************************/
/* Variable and structure definitions. */
/***********************************************************************/
struct state res;
int result, update_size;
unsigned char update_buffer[2048];
unsigned char answer_buffer[2048];
int buffer_length = sizeof(update_buffer);
/* Turn off the init flags so that the structure will be initialized */
res.options &= ~ (RES_INIT | RES_XINIT);
result = res_ninit(&res);
{
char zone_name[NS_MAXDNAME];
size_t zone_name_size = sizeof zone_name;
struct sockaddr_in s_address;
/* Check if the DNS server found is one of our regular DNS addresses */
s_address.sin_addr = addresses[0];
s_address.sin_family = res.nsaddr_list[0].sin_family;
s_address.sin_port = res.nsaddr_list[0].sin_port;
memset(s_address.sin_zero, 0x00, 8);
/* Set the DNS address found with res_findzonecut into the res */
/* structure. We will send the (TSIG signed) update to that DNS. */
res.nscount = 1;
res.nsaddr_list[0] = s_address;
/***********************************************************************/
/* The res_findzonecut(), res_nmkupdate(), and res_nsendsigned() */
/* can be replaced with one call to res_nupdate() using */
/* update_records[1] to skip the zone record: */
/* */
/* result = res_nupdate(&res, &update_records[1], &my_key); */
/* */
/***********************************************************************/
/***********************************************************************/
/* Now verify that our update actually worked! */
/* We choose to use TCP and not UDP, so set the appropriate option now */
/* that the res variable has been initialized. We also want to ignore */
/* the local cache and always send the query to the DNS server. */
/***********************************************************************/
res.options |= RES_USEVC|RES_NOCACHE;
Related concepts
Thread safety
/*************************************************/
/* Server example send file data to client */
/*************************************************/
#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;
char buffer[255];
/*************************************************/
/* If an argument is specified, use it to */
/* control the number of incoming connections */
/*************************************************/
if (argc >= 2)
num = atoi(argv[1]);
else
num = 1;
/*************************************************/
/* Create an AF_INET6 stream socket to receive */
/* incoming connections on */
/*************************************************/
listen_sd = socket(AF_INET6, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}
/*************************************************/
/* Set the SO_REUSEADDR bit so that you do not */
/* need 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(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
/*************************************************/
/* Set the listen backlog */
/*************************************************/
rc = listen(listen_sd, 5);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}
/*************************************************/
/* Initialize the local and remote addr lengths */
/*************************************************/
local_addr_length = sizeof(local_addr);
remote_addr_length = sizeof(remote_addr);
/*************************************************/
/* Inform the user that the server is ready */
/*************************************************/
printf("The server is ready\n");
/*************************************************/
/* Go through the loop once for each connection */
/*************************************************/
for (i=0; i < num; i++)
{
/**********************************************/
/* Wait for an incoming connection */
/**********************************************/
printf("Iteration: %d\n", i+1);
printf(" waiting on accept_and_recv()\n");
rc = accept_and_recv(listen_sd,
&accept_sd,
(struct sockaddr *)&remote_addr,
&remote_addr_length,
(struct sockaddr *)&local_addr,
&local_addr_length,
&buffer,
sizeof(buffer));
if (rc < 0)
{
perror("accept_and_recv() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}
printf(" Request for file: %s\n", buffer);
/**********************************************/
/* Open the file to retrieve */
/**********************************************/
fd = open(buffer, O_RDONLY);
if (fd < 0)
{
perror("open() failed");
close(listen_sd);
close(accept_sd);
exit(-1);
}
/**********************************************/
/* Initialize the sf_parms structure */
/**********************************************/
memset(&parms, 0, sizeof(parms));
parms.file_descriptor = fd;
parms.file_bytes = -1;
/**********************************************/
/* Initialize the counter of the total number */
/* of bytes sent */
/**********************************************/
/* Loop until the entire file has been sent */
/**********************************************/
do
{
rc = send_file(&accept_sd, &parms, SF_CLOSE);
if (rc < 0)
{
perror("send_file() failed");
close(fd);
close(listen_sd);
close(accept_sd);
exit(-1);
}
total_sent += parms.bytes_sent;
/**********************************************/
/* 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);
}
Related information
send_file()--Send a File over a Socket Connection API
accept_and_recv()
/*************************************************/
/* Client example requests file data from server */
/*************************************************/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char filename[256];
char buffer[32 * 1024];
/*************************************************/
/* Initialize the socket address structure */
/*************************************************/
/* Determine the host name and IP address of the */
/* machine the server is running on */
/*************************************************/
if (argc < 2)
{
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
}
else if ((isdigit(*argv[1])) || (*argv[1] == ':'))
{
rc = inet_pton(AF_INET6, argv[1], &addr.sin6_addr.s6_addr);
}
else
{
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_flags = AI_V4MAPPED;
rc = getaddrinfo(argv[1], NULL, &hints, &res);
if (rc != 0)
{
printf("Host not found! (%s)\n", argv[1]);
exit(-1);
}
memcpy(&addr.sin6_addr,
(&((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr),
sizeof(addr.sin6_addr));
freeaddrinfo(res);
}
/**************************************************/
/* 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_INET6 stream socket */
/*************************************************/
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket() failed");
exit(-1);
}
printf("Socket completed.\n");
/*************************************************/
/* Connect to the server */
/*************************************************/
rc = connect(sockfd,
(struct sockaddr *)&addr,
sizeof(struct sockaddr_in6));
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");
/*************************************************/
/* 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);
}
Xsockets tool
The Xsockets tool is one of the many tools that is supplied with the IBM i product. All tools are stored in
the QUSRTOOL library. The Xsockets tool allows programmers to interactively work with socket APIs.
The Xsockets tool allows you to do the following tasks:
• Learn about the socket APIs.
• Re-create specific scenarios interactively to help debug.
Note: The Xsockets tool is supplied in an as-is format.
Configuring Xsockets
The Xsockets tool is available in two versions. The first version is integrated with the IBM i client. The
integrated version is completely created by the first set of instructions. The second version uses a Web
browser as the client.
If you want to use the Web browser client, you must complete setup instructions for the integrated
version first.
To create the Xsockets tool, complete the following steps:
1. To unpackage the tool, enter
on a command line.
Note: You must have 10 characters between the opening and closing single quotation marks (').
2. To add the QUSRTOOL library to your library list, enter
ADDLIBLE QUSRTOOL
on a command line.
3. Create a library in which to create the Xsocket program files by entering
CRTLIB <library-name>
on a command line. The <library-name> is the the library in which you want the Xsockets tool
objects created. For example,
CRTLIB MYXSOCKET
on the command line. In the place of library-name, use the library you created in step “3” on page
168. For example, to create the tool in the MYXSOCKET library, enter:
http://<system_name>:2001/.
where <system_name> is the machine name of the system. For example: http://mysystemi:2001/.
3. On the IBM i Tasks page, select IBM Web Administration for i.
4. From the top menu, select the Setup tab.
5. Click Create Application Server.
6. Under integrated Web application server, select the type of application server to create, and click
Next.
7. Enter the name for the server instance, and click Next. For example, if this instance serves the
Xsockets tool in a browser, then you can use the name xsocket. A new HTTP Server (powered by
Apache) will be created in addition to the integrated Web application server.
Note: Use the default HTTP Server name and description.
8. Select a range for internal ports to be used by the application server and click Next.
Note: Use a port number that is greater than 1024.
9. Select the IP address, an available port that you want to use, and click Next.
Note: Use a port number that is greater than 1024. Do not select the default port number 80.
10. Click Next to use the default value for specifying the user ID.
11. Click Finish to confirm the Application Server and HTTP Server (powered by Apache) configuration
settings.
Related tasks
Updating configuration files
After you have installed the integrated Xsockets tool, you must complete manual changes to several
configuration files for the instance.
Testing Xsockets tool in a Web browser
After you have completed configuring the Xsockets Web application, you are ready to test the Xsockets
tool within a browser. The server and application instance should already be started.
Using Xsockets in a Web browser
Follow these instructions for using the Xsockets tool in a Web browser.
CPY OBJ('/QSYS.LIB/XXXX.LIB/QATTIFS2.FILE/XSOCK.MBR')
TOOBJ('/www/<server_name>/xsock.war') FROMCCSID(*OBJ) TOCCSID(819) OWNER(*NEW)
where XXXX is the library name you created during Xsockets configuration and <server_name> is the
name of the server instance you created during Apache configuration. This is the integrated file system
directory where you would like to store the XSockets JAR file.
2. Optional: Update the web.xml file:
Note: This step is only necessary if Xsockets was installed to a library other than XSOCKETS during
Xsockets configuration.
CD DIR('/www/<server_name>')
where <server_name> is the name of the server instance you created during Apache configuration.
b) From a command line, enter
wrklnk 'WEB-INF/web.xml'
<init-param>
<param-name>library</param-name>
<param-value>xsockets</param-value>
</init-param>
In place of the xsockets, insert the library name that you created during Xsockets configuration.
g) Save the file and exit the edit session.
h) From a command line, enter
to create a new XSockets JAR file containing the updated configuration file.
3. Optional: Add the authority check to httpd.conf file.
This forces Apache to authenticate users trying to access the Xsockets Web application.
Note: It is also necessary for getting write access to create UNIX sockets.
a) From a command line, enter
wrklnk '/www/<server_name>/conf/httpd.conf'
where <server_name> is the name of the server instance you created during the Apache
configuration. For example, if you choose xsocks for the server name, you can enter:
wrklnk '/www/xsocks/conf/httpd.conf'
<Location /xsock>
AuthName "X Socket"
AuthType Basic
PasswdFile %%SYSTEM%%
UserId %%CLIENT%%
Require valid-user
order allow,deny
allow from all
</Location>
Related tasks
Updating configuration files
After you have installed the integrated Xsockets tool, you must complete manual changes to several
configuration files for the instance.
where <server_name> is the name of the server instance you created during the HTTP Server
(powered by Apache) configuration. This takes a while.
2. Check the status of the server by issuing the Work with Active Jobs (WRKACTJOB) command from the
command line interface. You should see one job with your server_name, PGM-QLWISVR Function, with
JVAW status, and all additional jobs should have the SIGW status. If this is the case, then you can
proceed to the next step.
3. In a browser, enter the following URL:
http://<system_name>:<port>/xsock/index
where <system_name> is the machine name of the system and <port> is the port number that you
chose during the Apache configuration.
4. When prompted, enter your user name and password for the server. The Web client for Xsocket should
appear.
Related tasks
Configuring an Integrated Web Application Server
Using Xsockets
You can work with the Xsockets tool either from the integrated client or from a Web browser.
To work with an integrated version of Xsockets, you must configure the Xsockets tool. In addition to
configuring the Xsockets tool for an integrated client, you must also complete the steps in Configuring
Xsockets to use a Web browser if you prefer to work with the tool in a browser environment. Many of the
concepts are similar between the two versions of the tools. Both tools allow you to issue socket calls
interactively and both tools provide errnos for issued socket calls; however, the interfaces do have some
differences.
Note: If you want to work with socket programs that use the GSKit secure socket APIs, you must use the
Web version of the tool.
Related concepts
Configuring Xsockets to use a Web browser
You can configure the Xsockets tool to allow access through a Web browser. You can implement these
instructions multiple times on the same system to create different server instances. With multiple
instances, you can run multiple versions at the same time on different listening ports.
Related tasks
Configuring Xsockets
The Xsockets tool is available in two versions. The first version is integrated with the IBM i client. The
integrated version is completely created by the first set of instructions. The second version uses a Web
browser as the client.
1. From a command line, add the library in which the Xsockets tool exists to your library list by issuing
this command:
ADDLIBLE <library-name>
where the <library-name> is the name of the library you created during integrated Xsockets
configuration. For example, if the name of the library is MYXSOCKET, then enter:
ADDLIBLE MYXSOCKET
CALL TSOXSOCK
3. From the Xsocket window that is shown, you can access all socket routines through its menu bar and
selection field. This window is always shown after you choose a socket API. You can use this interface
to select socket programs that already exist. To work with a new socket, follow these steps:
a) In the list of socket APIs, select socket and press Enter.
b) In the socket() prompt window that displays, select the appropriate Address Family, Socket
Type, and Protocol for the socket, and press Enter.
c) Select Descriptor and select Select descriptor.
Note: If other socket descriptors already exist, this displays a list of active socket descriptors.
d) From the list that displays, select the socket descriptor that you created.
Note: If other socket descriptors exist, the tool automatically applies a socket API to the latest
socket descriptor.
4. From the list of socket APIs, select a socket API with which you want to work, whatever socket
descriptor you chose in step 3c is used on that socket API. As soon as you select a socket API, a series
of windows are displayed where you can provide specific information about the socket API. For
http://system-name:2001/
where system-name is the name of the system that contains the server instance.
2. Select Administration.
3. From the left navigation, select Manage HTTP Servers.
4. Select your instance name, and click Start. You can also start the server instance from a command line
by entering:
where <instance_name> is the name of your HTTP Server created in the Apache configuration. For
example, you can use the server instance name xsocks.
5. To access the Xsockets Web application, enter this URL in a browser:
http://<system_name>:<port>/xsock/index
where <system_name> is the machine name of the system and <port> is the port specified when you
created the HTTP instance. For example, if the system name is mySystemi and the HTTP Server
instance listens on port 1025, you can enter:
http://mySystemi:1025/xsock/index
6. After the Xsockets tool loads in the Web browser, you can work with the existing socket descriptor or
create a new one.
To create a new socket descriptor, follow these steps:
a. From the Xsocket Menu, select socket.
b. In the Xsocket Query window that displays, select the appropriate Address Family, Socket Type,
and Protocol for this socket descriptor. Click Submit. As soon as the page reloads, the new socket
descriptor is displayed in the Socket pull-down menu.
c. From the Xsocket Menu, select API calls to which you want to apply this socket descriptor. As with
the integrated version of the Xsockets tool, the tool automatically applies API calls to the latest
socket descriptor if you do not select a socket descriptor.
Related concepts
Configuring Xsockets to use a Web browser
To delete ONLY objects that the tool creates, enter the following command:.
To delete BOTH source members and objects created by the tool, enter the following command:
Customizing Xsockets
You can change the Xsockets tool by adding additional support for the socket network routines (such as
inet_addr()).
If you choose to customize this tool to meet your own needs, it is recommended that you do not make
changes in the QUSRTOOL library. Instead, copy the source files into a separate library and make the
changes there. This preserves the original files in the QUSRTOOL library so they are available if needed in
the future. You can use the TSOCRT program to recompile the tool after making your changes (note that if
the source files are copied to a separate library, you also need to make changes in TSOCRT to use it). Use
the TSODLT program to remove old versions of the tool objects before creating the tool.
Serviceability tools
Because the use of sockets and secure sockets continues to grow to accommodate e-business
applications and servers, the current serviceability tools need to keep up with this demand.
Enhanced serviceability tools help you complete traces on socket programs to find solutions to errors
within socket and SSL-enabled applications. These tools help you and support center personnel to
determine where socket problems are by selecting socket traits, such as IP address or port information.
The following table gives an overview for the each of these service tools.
Related information
SSL_Strerror()--Retrieve SSL Runtime Error Message API
SSL_Perror()--Print SSL Error Message API
gsk_strerror()--Retrieve GSKit runtime error message API
hstrerror()--Retrieve Resolver Error Message API
Start Trace (STRTRC) command
For license inquiries regarding double-byte (DBCS) information, contact the IBM Intellectual Property
Department in your country or send inquiries, in writing, to:
The following paragraph does not apply to the United Kingdom or any other country where such
provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION
PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR
IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer of
express or implied warranties in certain transactions, therefore, this statement may not apply to you.
This information could include technical inaccuracies or typographical errors. Changes are periodically
made to the information herein; these changes will be incorporated in new editions of the publication.
IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this
publication at any time without notice.
Any references in this information to non-IBM Web sites are provided for convenience only and do not in
any manner serve as an endorsement of those Web sites. The materials at those Web sites are not part of
the materials for this IBM product and use of those Web sites is at your own risk.
IBM may use or distribute any of the information you supply in any way it believes appropriate without
incurring any obligation to you.
Licensees of this program who wish to have information about it for the purpose of enabling: (i) the
exchange of information between independently created programs and other programs (including this
one) and (ii) the mutual use of the information which has been exchanged, should contact:
IBM Corporation
Software Interoperability Coordinator, Department YBWA
3605 Highway 52 N
Rochester, MN 55901
U.S.A.
Trademarks
IBM, the IBM logo, and ibm.com are trademarks or registered trademarks of International Business
Machines Corp., registered in many jurisdictions worldwide. Other product and service names might be
trademarks of IBM or other companies. A current list of IBM trademarks is available on the Web at
"Copyright and trademark information" at www.ibm.com/legal/copytrade.shtml.
180 Notices
Adobe, the Adobe logo, PostScript, and the PostScript logo are either registered trademarks or
trademarks of Adobe Systems Incorporated in the United States, and/or other countries.
UNIX is a registered trademark of The Open Group in the United States and other countries.
Java and all Java-based trademarks and logos are trademarks of Oracle, Inc. in the United States, other
countries, or both.
Other product and service names might be trademarks of IBM or other companies.
Notices 181
182 IBM i: Socket programming
IBM®