Network Interface Guide

Asynchronous Networking

This section discusses the techniques of asynchronous network communication using XTI/TLI for real-time applications. SunOS provides support for asynchronous network processing of XTI/TLI events using a combination of STREAMS asynchronous features and the non-blocking mode of the XTI/TLI library routines.

Networking Programming Models

Like file and device I/O, network transfers can be done synchronously or asynchronously with process service requests.

Synchronous Networking

Synchronous networking proceeds similar to synchronous file and device I/O. Like the write(2) function, the request to send returns after buffering the message, but might suspend the calling process if buffer space is not immediately available. Like the read(2) function, a request to receive suspends execution of the calling process until data arrives to satisfy the request. Because SunOS provides no guaranteed bounds for transport services, synchronous networking is inappropriate for processes that must have real-time behavior with respect to other devices.

Asynchronous Networking

Asynchronous networking is provided by non-blocking service requests. Additionally, applications can request asynchronous notification when a connection might be established, when data might be sent, or when data might be received.

Asynchronous Connectionless-Mode Service

Asynchronous connectionless mode networking is conducted by configuring the endpoint for non-blocking service, and either polling for or receiving asynchronous notification when data might be transferred. If asynchronous notification is used, the actual receipt of data typically takes place within a signal handler.

Making the Endpoint Asynchronous

After the endpoint has been established using t_open(3NSL), and its identity established using t_bind(3NSL), the endpoint can be configured for asynchronous service. This is done by using the fcntl(2) function to set the O_NONBLOCK flag on the endpoint. Thereafter, calls to t_sndudata(3NSL) for which no buffer space is immediately available return -1 with t_errno set to TFLOW. Likewise, calls to t_rcvudata(3NSL) for which no data are available return -1 with t_errno set to TNODATA.

Asynchronous Network Transfers

Although an application can use the poll(2) function to check periodically for the arrival of data or to wait for the receipt of data on an endpoint, it might be necessary to receive asynchronous notification when data has arrived. This can be done by using the ioctl(2) function with the I_SETSIG command to request that a SIGPOLL signal be sent to the process upon receipt of data at the endpoint. Applications should check for the possibility of multiple messages causing a single signal.

In the following example, protocol is the name of the application-chosen transport protocol.


#include <sys/types.h>
#include <tiuser.h>
#include <signal.h>
#include <stropts.h>

int				fd;
struct t_bind				*bind;
void				sigpoll(int);

	fd = t_open(protocol, O_RDWR, (struct t_info *) NULL);

	bind = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
	...     /* set up binding address */
	t_bind(fd, bind, bin

	/* make endpoint non-blocking */
	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);

	/* establish signal handler for SIGPOLL */
	signal(SIGPOLL, sigpoll);

	/* request SIGPOLL signal when receive data is available */
	ioctl(fd, I_SETSIG, S_INPUT | S_HIPRI);

	...

void sigpoll(int sig)
{
	int					flags;
	struct t_unitdata					ud;

	for (;;) {
		... /* initialize ud */
		if (t_rcvudata(fd, &ud, &flags) < 0) {
			if (t_errno == TNODATA)
				break;  /* no more messages */
			... /* process other error conditions */
	}
	... /* process message in ud */
}

Asynchronous Connection-Mode Service

For connection-mode service, an application can arrange for not only the data transfer, but for the establishment of the connection itself to be done asynchronously. The sequence of operations depends on whether the process is attempting to connect to another process or is awaiting connection attempts.

Asynchronously Establishing a Connection

A process can attempt a connection and asynchronously complete the connection. The process first creates the connecting endpoint, and, using fcntl(2), configures the endpoint for non-blocking operation. As with connectionless data transfers, the endpoint can also be configured for asynchronous notification upon completion of the connection and subsequent data transfers. The connecting process then uses the t_connect(3NSL) function to initiate setting up the transfer. Then the t_rcvconnect(3NSL) function is used to confirm the establishment of the connection.

Asynchronous Use of a Connection

To asynchronously await connections, a process first establishes a non-blocking endpoint bound to a service address. When either the result of poll(2) or an asynchronous notification indicates that a connection request has arrived, the process can get the connection request by using the t_listen(3NSL) function. To accept the connection, the process uses the t_accept(3NSL) function. The responding endpoint must be separately configured for asynchronous data transfers.

The following example illustrates how to request a connection asynchronously.


#include <tiuser.h>
int             fd;
struct t_call   *call;

	fd = .../* establish a non-blocking endpoint */

	call = (struct t_call *) t_alloc(fd, T_CALL, T_ADDR);
	.../* initialize call structure */
	t_connect(fd, call, call);

	/* connection request is now proceeding asynchronously */

	.../* receive indication that connection has been accepted */
	t_rcvconnect(fd, &call);

The following example illustrates listening for connections asynchronously.


#include <tiuser.h>
int             fd, res_fd;
struct t_call   call;

	fd = ... /* establish non-blocking endpoint */

	.../*receive indication that connection request has arrived
*/
	call = (struct t_call *) t_alloc(fd, T_CALL, T_ALL);
	t_listen(fd, &call);

	.../* determine whether or not to accept connection */
	res_fd = ... /* establish non-blocking endpoint for response
*/
	t_accept(fd, res_fd, call);

Asynchronous Open

Occasionally, an application might be required to dynamically open a regular file in a file system mounted from a remote host, or on a device whose initialization might be prolonged. However, while such an open is in progress, the application is unable to achieve real-time response to other events. Fortunately, SunOS provides a means of solving this problem by having a second process perform the actual open and then pass the file descriptor to the real-time process.

Transferring a File Descriptor

The STREAMS interface under SunOS provides a mechanism for passing an open file descriptor from one process to another. The process with the open file descriptor uses the ioctl(2) function with a command argument of I_SENDFD. The second process obtains the file descriptor by calling ioctl(2) with a command argument of I_RECVFD.

In this example, the parent process prints out information about the test file, and creates a pipe. Next, the parent creates a child process, which opens the test file, and passes the open file descriptor back to the parent through the pipe. The parent process then displays the status information on the new file descriptor.


Example 3-13 File Descriptor Transfer

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stropts.h>
#include <stdio.h>

#define TESTFILE "/dev/null"
main(int argc, char *argv[])
{
	int fd;
	int pipefd[2];
	struct stat statbuf;

	stat(TESTFILE, &statbuf);
	statout(TESTFILE, &statbuf);
	pipe(pipefd);
	if (fork() == 0) {
		close(pipefd[0]);
		sendfd(pipefd[1]);
	} else {
		close(pipefd[1])
		recvfd(pipefd[0]);
	}
}

sendfd(int p)
{
	int tfd;

	tfd = open(TESTFILE, O_RDWR);
	ioctl(p, I_SENDFD, tfd);
}

recvfd(int p)
{
	struct strrecvfd rfdbuf;
	struct stat statbuf;
	char			fdbuf[32];

	ioctl(p, I_RECVFD, &rfdbuf);
	fstat(rfdbuf.fd, &statbuf);
	sprintf(fdbuf, "recvfd=%d", rfdbuf.fd);
	statout(fdbuf, &statbuf);	
}

statout(char *f, struct stat *s)
{
	printf("stat: from=%s mode=0%o, ino=%ld, dev=%lx, rdev=%lx\n",
		f, s->st_mode, s->st_ino, s->st_dev, s->st_rdev);
	fflush(stdout);
}