Unix Network Programming Manual
Asynchronous Receive Example
The following example demonstrates an example of an asynchronous
I/O using select.
/* ==========================================================> test_select.c
*
* Simple example to test signals and the select function. There
* are two buffers, A and B. A is filled from stdin and sent to the
* remote server. B is filled by the responses of the server and
* sent to stdout. Due to the difference in the rates, each source
* is buffered, with some limited length buffer. Stdout is not done
* asynchronously to simplify the example.
*
* ====================================================================
*/
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
int writing, write_event;
int sock;
/*
* The buffers are ring buffers with inX and outX pointing to the next
* available locations for input and output, respectively.
*/
char bufferA [256], bufferB[256];
char *inA, *outA, *inB, *outB;
char *endA = bufferA + 256, *endB = bufferB+256;
void iohandler ();
int OpenSock (char *, char *);
void SockWrite ();
main
(
int argc,
char *argv[]
)
{
void (*old_handler)();
int flags;
if (argc < 3)
{
fprintf (stderr, "Syntax: test_select port host\n");
exit (0);
}
/*
* Open the socket with the appropriate characteristics and then
* set stdin to be asynchronous.
*/
sock = OpenSock (argv [1], argv [2]);
fcntl (0, F_GETFL, &flags);
fcntl (0, F_SETFL, flags | FASYNC);
fcntl (1, F_GETFL, &flags);
/*
* Set the handler for I/O.
*/
old_handler = signal (SIGIO, iohandler);
/*
* Initialize the buffers and start the I/O going by sending the
* user a prompt.
*/
inA = bufferA; outA = bufferA;
inB = bufferB; outB = bufferB;
printf ("Enter the text you want converted\n");
writing = 0;
write_event = 0;
do
{
sigpause (0);
/*
* Something has happened. If its a read, it was handled in the
* handler. If its a write completion, do nothing if there's
* nothing to write, otherwise, write something.
*/
if (write_event)
if (inB != outB)
{
SockWrite ();
writing = 1;
}
} while (1);
/*
* Restore everything and quit
*/
fcntl(sock, F_SETFL, flags);
signal (SIGIO, old_handler);
}
void iohandler (void)
{
unsigned int msglen, ct;
struct timeval timeout;
fd_set readfd, writefd, exfd;
static char buf [80], *bp;
/*
* An interrupt has occured. It could be a "ready to read" or a
* "done writing". Use select to find out which and handle it
* accordingly.
*/
printf ("in iohandler\n");
/*
* Set the timeout to zero since the interrupt has happened and something
* should already be there.
*/
timeout . tv_sec = 0;
timeout . tv_usec = 0;
/*
* Set up the fd masks for the socket and call select to get the status.
* We know what to do with each read and write, so don't bother to
* check exception status.
*/
FD_ZERO (&readfd);
FD_ZERO (&writefd);
FD_SET (sock, &readfd);
FD_SET (sock, &writefd);
FD_SET (0, &readfd);
FD_SET (1, &writefd);
select (64, &readfd, &writefd, 0, &timeout);
/*
* If its data available, it needs to be put into bufferA (stdin) or
* bufferB (socket). If one of the buffers can't hold the data,
* throw it away.
*/
if (FD_ISSET (sock, &readfd) > 0)
{
if ((msglen = recv (sock, buf, 80, 0)) < 0)
{
perror ("receiving message");
close(sock);
exit (0);
}
/*
* If there's room, store it in buf, otherwise, ignore it.
*/
if (((inB - outB) % 256) > msglen)
{
/*
* Inefficient way to move the data, but is more obvious that using
* memcpy with the wrap around problem.
*/
bp = buf;
for (ct = 0; ct < msglen; ct ++)
{
*inB ++ = *bp ++;
if (inB == endB)
inB = bufferB;
}
}
}
if (FD_ISSET (0, &readfd) > 0)
{
gets (buf);
if (((inA - outA) % 256) > strlen (buf))
{
/*
* Inefficient way to move the data, but is more obvious that using
* memcpy with the wrap around problem.
*/
bp = buf;
for (ct = 0; ct < msglen; ct ++)
{
*inA ++ = *bp ++;
if (inA == endA)
inA == bufferA;
}
}
/*
* There's new data. If already writing, do nothing, otherwise,
* there's data to be sent. Send lines terminated by nulls.
*/
if (! writing)
{
SockWrite ();
writing = 1;
}
}
/*
* Is there a write completion on the socket?
*
* Just to show the alternatives, the write will not be handled
* here, but in the main program. This is not necessarily desirable
* or undesirable, just different. Set a flag here so that the
* main program can see that there is something to be done.
*/
write_event = 1;
writing = 0;
return;
}
/*
* Open the socket and set up for asynchronous I/O.
*/
int OpenSocket
(
char *port,
char *hostname
)
{
int flags;
int sock;
struct sockaddr_in saddr;
int addrlen, len, msglen;
struct hostent *host;
char msg[80];
/* clear the socket address for INET format */
memset((char *) &saddr, 0, sizeof(struct sockaddr_in));
/* set up the socket family and get the service data for setting up the
port number */
saddr.sin_family = AF_INET;
saddr.sin_port = htons (atoi (port));
/* Set up the address to be INADDR_ANY so that the server can
* communicate with anyone.
*/
host = gethostbyname (hostname);
saddr . sin_addr . s_addr = htonl (host -> h_addr_list [0]);
/*
* The socket address for the service is now complete, so create the socket.
*/
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{ perror("on opening socket");
exit(0);
}
/*
* Fetch the flags for sock and modify to be asynchronous, which
* requests a signal if an interrupt (data available) occurs.
* Importantly, you have to set the process group of the socket
* with F_SETOWN to the process pid.
*/
fcntl (sock, F_SETOWN, getpid ());
flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags | FASYNC);
if (connect (sock, (struct sockaddr *) &saddr, sizeof (saddr)) < 0)
{
perror ("on connect");
exit (0);
}
return (sock);
}
void SockWrite ()
{
int ct;
static char buf [256], *bp;
bp = buf;
ct = 0;
while ((outA != inA) && (*outA != '\0'))
{
*bp ++ = *outA ++;
if (outA == endA)
outA = bufferA;
ct ++;
}
*bp = '\0'; /* Send the null*/
send (sock, buf, ct + 1, 0);
return;
}