Sockets Programming - Multi-tasked example

This program is a fairly simple message logging program in some respects similar to that described earlier. As with the earlier example, the complete source code is here for you to cut and paste into your programs, with accreditation please. The program is compiled with the flags -lnsl -lsocket. There are, however, several important differences from the earlier example.

A separate process is created to handle each session. Since all the processes are in the same process group, sneding SIGHUP to the process group leader will close down all the sessions.

The program starts with the customary header files.

#include        <netdb.h>
#include        <netinet/in.h>
#include        <sys/types.h>
#include        <sys/socket.h>
#include        <stdio.h>
#include        <time.h>
#include        <signal.h>
#include        <string.h>
#include	<errno.h>

The socket union declaration

union sock
{
	struct sockaddr s;
	struct sockaddr_in i;
};

This part of the code is followed by prototypes for the signal handling functions, the function handle() which drives the actual sessions and the single global variable sigtype used to indicate the signal type.

void	timeout(int);
void	closedown(int);
void	handle(int);

int	sigtype;

The function main()

The single required command line argument is the port number the program will listen on. This part of the code also defines the signal handler for the SIGHUP signal and establishes that the processes form a separate process group.

main(int argc,char *argv[])
{
	int     port;
	union   sock    sock,work;
	int     wsd,sd;
	int     addlen;
	pid_t	pd;
	int     i,rv,nrv;

	if(argc != 2)	exit(1);
	else		port = atoi(argv[1]);
	signal(SIGHUP,closedown);
	setpgrp();

The remainder of the code in the function main() is concerned with setting up the sockets and is more or less identical to that in the previous examples.

	if((sd = socket(AF_INET,SOCK_STREAM,0)) == -1)
	{
		perror("No socket");
		exit(1);
	}
	sock.i.sin_family = AF_INET;
	sock.i.sin_port = port;
	sock.i.sin_addr.s_addr = INADDR_ANY;
	if(bind(sd,&(sock.s),sizeof(struct sockaddr)) == -1)
	{
		perror("Bad Bind");
		exit(1);
	}
	if(listen(sd,2) == -1)
	{
		perror("Bad listen");
		exit(1);
	}

Until the connection accepting loop is reached, Here once a connection has been accepted, fork() is used to create the child process that will handle the session via the function handle(). The parent process closes the socket and carries on listening on the port until it is closed down by the closedown() function

 

	do
	{               
		wsd = accept(sd,&(work.s),&addlen);
		if(pid = fork()) 	close(wsd);	/* parent */
		else			handle(wsd);	/* child */
	} while(1);
}

The function handle()

This function does the actual handling of the communication with the remote host. It arranges for the just forked process to handle the SIGLARM signal via the function timeout().

void	handle(int wsd)
{
	int	nrv;
	int	i=0;
	char	buff[BUFSIZ];
	FILE	*logfp = NULL;
	signal(SIGALRM,timeout);

The basic processing loop starts thus.

	do
	{
		alarm(20);
		nrv = read(wsd,buff+i,BUFSIZ-i);

The loop is similar to the previous example. The function alarm() is used to request a timeout in 20 seconds time. nrv is the number of bytes actually read. For normal operation the number of bytes read will be greater than zero and the following code deals with this case.

		if(nrv > 0)
		{
			char    *crlfp;
			int     fraglen;
			i += nrv;
			buff[i] = '\0';
			if(crlfp=strstr(buff,"\r\n"))
			{
				*crlfp = '\0';
				if(logfp == NULL)
				{
					logfp = fopen(buff,"w");
					if(logfp == NULL)
					{
						close(wsd);
						return;
				     	}
				}
				else	fprintf(logfp,"%s\n",buff);
				fraglen = (buff+i)-(crlfp+2);
				if(fraglen)
				{
					strcpy(buff,crlfp+2);
					i = fraglen;
				}
				else	i = 0;
			}                

		}

This deals with the requirement that the first line of text is the name of the file to be used for logging and also deals with possiblity of the text arriving in "bits and drabs". If the number of bytes read is zero then the remote has closed the link, the program simply closes the local end of the link thus and the process then terminates.

		else if (nrv == 0)		
		{
			close(wsd);
			fclose(logfp);
			exit(0);
		}

If the number of bytes read is returned as less than zero then there has been some sort of error, or a signal has been caught during read().

		else if(nrv < 0)
		{	
			switch(sigtype)
			{
				case SIGALRM :	
					write(wsd,"Timed out - bye\r\n",17);
					break;
				case SIGHUP :
					write(wsd,"Closing down - bye\r\n",20);
					break;
			}
			close(wsd);
			fclose(logfp);
			exit(0);
			
		}

The functions timeout() and closedown()

The remaining part of the program consists of the two signal handling functions, timeout() and closedwon(). These do nothing more than catch the signal and set the global variable sigtype which is interrogated in the function handle() to determine the reason for the error return from read()

void	timeout(int sig)
{
	if(sig == SIGALRM) sigtype = SIGALRM;
}

void	closedown(int sig)
{
	if(sig == SIGHUP) sigtype = SIGHUP;
}
Hosted by www.Geocities.ws

1