[alt.sources] Simple Socket Library

mat@zeus.opt-sci.arizona.edu (Mat Watson) (04/29/91)

***** Simple Socket Library *******
by Mat Watson and Hubert Bartels

The Simple Socket library is intended to simplify programming with BSD
sockets, by providing a set of functions which mimic the stdio library.
The basic data type used by the library is the SOCKET,
which is analogous to the stdio FILE data type.  Here's a comparison
of some code fragments to illustrate the similarities:

--Normal file IO--		--Using Simple Sockets--
#include <stdio.h>		#include <ss.h>
...				...
FILE *fp;			SOCKET *sp;
...				...
fp = fopen("foobar.dat","r");	sp = ConnectSock("whereami.edu",4010)
...				...
fgets( buffer, 99, fp);		SockGets( buffer, 99, sp);

The calls which open and close the sockets themselves hide many of the
gory details associated with using sockets, and provide a more natural
interface.  The call to ConnectSock() above takes two arguments: a
host name, and a port number.  Which is a good deal simpler than the
series of steps one normally takes to establish a connection using
standard system calls.  As is the case with most other routines that
simplify a complicated process, the Simple Socket Library makes
certain assumptions about how one wishes to use sockets to
communicate.

This library assumes communications using 'streams' as opposed to
datagrams, as they are quite similar to the stdio 'streams'.  In
addition, the library does not use ioctl() to modify socket
characteristics.  So if you want to make the sockets non blocking (for
example), you'll have to modify the library code ( or this
functionality could probably be added later ).  Instead, sockets
should be checked to see if they are ready to be accessed before
trying to read from one.

Since I don't have the time to write a bonafide manual, I put together
some test/example programs that should be looked over.  The file named
Testing explains how to use the test programs.  I also wrote the file
named Notes, which gives a man page like synopsis and a short
description for the library routines.  The file named Install has
instructions for building the library.

Much of the code is based on a version of the stdio library written by
Hubert Bartels.  Without that code as a basis, this library probably
wouldn't be here now.  Thanks Hubert.

If you find the code useful, have any constructive criticism, bug
fixes, or suggestions for improvement, send them to me (Mat).  I'm
finishing up my dissertation, so don't be surprised if I'm a bit slow
in replying :-).

This code is know to run on a: Sun3/160, Sparc 2, Data General Aviion,
and IBM risc workstation.  It compiled on an ATT 3b2, but didn't
run too well at all.

--Mat      4/29/91

Authors:
Mat Watson   mat@zeus.opt-sci.arizona.edu
and
Hubert Bartels hgb@catalina.opt-sci.arizona.edu

------------------------------- Cut Here -------------------------
#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
#	Run the following text with /bin/sh to create:
#	  README
#	  Makefile
#	  Notes
#	  Testing
#	  Install
#	  ss.c
#	  ss.base.h
#	  ss.sun.h
#	  ss.ibm.h
#	  ss.3b2.h
#	  test.h
#	  stdlib.h
#	  server1.c
#	  client1.c
#	  server2.c
#	  client2.c
#	  server3.c
#	  client3.c
#
sed 's/^X//' << 'SHAR_EOF' > README &&
X***** Simple Socket Library *******
Xby Mat Watson and Hubert Bartels
X
XThe Simple Socket library is intended to simplify programming with BSD
Xsockets, by providing a set of functions which mimic the stdio library.
XThe basic data type used by the library is the SOCKET,
Xwhich is analogous to the stdio FILE data type.  Here's a comparison
Xof some code fragments to illustrate the similarities:
X
X--Normal file IO--		--Using Simple Sockets--
X#include <stdio.h>		#include <ss.h>
X...				...
XFILE *fp;			SOCKET *sp;
X...				...
Xfp = fopen("foobar.dat","r");	sp = ConnectSock("whereami.edu",4010)
X...				...
Xfgets( buffer, 99, fp);		SockGets( buffer, 99, sp);
X
XThe calls which open and close the sockets themselves hide many of the
Xgory details associated with using sockets, and provide a more natural
Xinterface.  The call to ConnectSock() above takes two arguments: a
Xhost name, and a port number.  Which is a good deal simpler than the
Xseries of steps one normally takes to establish a connection using
Xstandard system calls.  As is the case with most other routines that
Xsimplify a complicated process, the Simple Socket Library makes
Xcertain assumptions about how one wishes to use sockets to
Xcommunicate.
X
XThis library assumes communications using 'streams' as opposed to
Xdatagrams, as they are quite similar to the stdio 'streams'.  In
Xaddition, the library does not use ioctl() to modify socket
Xcharacteristics.  So if you want to make the sockets non blocking (for
Xexample), you'll have to modify the library code ( or this
Xfunctionality could probably be added later ).  Instead, sockets
Xshould be checked to see if they are ready to be accessed before
Xtrying to read from one.
X
XSince I don't have the time to write a bonafide manual, I put together
Xsome test/example programs that should be looked over.  The file named
XTesting explains how to use the test programs.  I also wrote the file
Xnamed Notes, which gives a man page like synopsis and a short
Xdescription for the library routines.  The file named Install has
Xinstructions for building the library.
X
XMuch of the code is based on a version of the stdio library written by
XHubert Bartels.  Without that code as a basis, this library probably
Xwouldn't be here now.  Thanks Hubert.
X
XIf you find the code useful, have any constructive criticism, bug
Xfixes, or suggestions for improvement, send them to me (Mat).  I'm
Xfinishing up my dissertation, so don't be surprised if I'm a bit slow
Xin replying :-).
X
XThis code is know to run on a: Sun3/160, Sparc 2, Data General Aviion,
Xand IBM risc workstation.  It compiled on an ATT 3b2, but didn't
Xrun too well at all.
X
X--Mat      4/29/90
X
XAuthors:
XMat Watson   mat@zeus.opt-sci.arizona.edu
Xand
XHubert Bartels hgb@catalina.opt-sci.arizona.edu
X
X
SHAR_EOF
chmod 0775 README || echo "restore of README fails"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
X# Makefile
XVERSION = 1.0
XPATCHLEVEL = 0
X
X# Make sure you create the directories that libss.a and ss.h
X# will get copied to.
X# The directory where the file libss.a will get copied to on installation.
XINSTALL_LIB_DIR = /usr/local/lib/here
X# The directory where the file ss.h will get copied to on installation.
XINSTALL_INC_DIR = /usr/local/include
X
X# Uncomment these lines to compile on a 3B2
X#DEFINES = 
X#LIBS = -lss -lnet -lnsl_s
X#INCLUDEDIRS = -I/usr/netinclude
X#HEADERFILE = ss.3b2.h
X
X# Uncomment the following lines for a Sun3 or Sparc.
XDEFINES = -DVerbose_Errors
XLIBS = -lss
XINCLUDEDIRS =
XHEADERFILE = ss.sun.h
X
X# Uncomment the following lines for an Aviion.
X#DEFINES = -DVerbose_Errors -D_BSD_SOURCE # -D__STDC__
X#LIBS = -lss
X#INCLUDEDIRS =
X#HEADERFILE = ss.sun.h
X
X# Uncomment the following lines for an IBM risc workstation.
X#DEFINES = -DVerbose_Errors
X#LIBS = -lss
X#INCLUDEDIRS =
X#HEADERFILE = ss.ibm.h
X
X# gcc compiler options
X#CC = gcc
X#CFLAGS = -g $(DEFINES) $(INCLUDEDIRS) #-traditional
X
X#CC = cc
X#CFLAGS = -g $(DEFINES) $(INCLUDEDIRS)
X
XOBJS = ss.o
X
XCFILES = ss.c
X
XMISCFILES = README Makefile Notes Testing Install
X
XHFILES = ss.base.h ss.sun.h ss.ibm.h ss.3b2.h test.h stdlib.h
X
XDEMOFILES = server1.c client1.c server2.c client2.c server3.c client3.c
X
XARCHIVEFILES = $(MISCFILES) $(CFILES) $(HFILES) $(DEMOFILES) 
X
X.c.o:
X	$(CC) -c $(CFLAGS) $<
X
Xall: libss.a client1 server1 client2 server2 client3 server3
X
Xlibss.a: ss.h $(OBJS) 
X	ar rcv libss.a $(OBJS)
X	ranlib libss.a
X
Xss.h: ss.base.h $(HEADERFILE)
X	cat $(HEADERFILE) ss.base.h > ss.h
X
X$(OBJS): ss.h
X
Xinstall:
X	cp libss.a $(INSTALL_LIB_DIR)
X	cp ss.h    $(INSTALL_INC_DIR)
X
Xtar:
X	tar cf - $(ARCHIVEFILES) | compress > ss-$(VERSION).tar.Z
Xshar:
X	xshar $(ARCHIVEFILES) > ss-$(VERSION).shar
X
Xclean:
X	rm *.o core
X
Xrealclean: clean
X	rm  server1 server2 server3 client1 client2 client3 libss.a
Xlint:
X	lint $(CFILES) $(DEFINES)
X
Xserver1: server1.o libss.a
X	$(CC) server1.o -L. $(LIBS) -o server1
X
Xclient1: client1.o libss.a
X	$(CC) client1.o -L. $(LIBS) -o client1
X
Xserver2: server2.o libss.a
X	$(CC) server2.o -lss -L. -o server2
X
Xclient2: client1.o libss.a
X	$(CC) client1.o -lss -L. -o client2
X
Xserver3: server3.o libss.a
X	$(CC) server3.o -lss -L. -o server3
X
Xclient3: client3.o libss.a
X	$(CC) client3.o -lss -L. -o client3
X
SHAR_EOF
chmod 0775 Makefile || echo "restore of Makefile fails"
sed 's/^X//' << 'SHAR_EOF' > Notes &&
X######## Commands for opening and closing sockets ###########
X
XSocket * ServerSock( int port_number )
X
XOpens a socket on port number 'port_number' on the machine that the
Xserver program is running on.  Returns a pointer to an open socket
Xthat clients may use to connect to. If an error occurs, SeverSock
Xreturns SS_NULL.
X
XSOCKET * ConnectSock( char * hostname, int port_number )
X
XAllows a client program to connect to a socket on port 'port_number'
Xon system 'hostname'.  Returns a pointer to an open socket which the
Xclient program can use to communicate with the server program.  If the
Xcommand fails for any reason, it returns SS_NULL.
X
XSOCKET * AcceptSock( SOCKET * server_socket )
X
XUses the accept() system call to accept a connect request from a
Xclient.  The variable 'server_socket' must point to the socket opened
Xby the server program using the ServerSock() command.  Returns a
Xpointer to an open socket that the server program may use to
Xcommunicate with the client program.  AcceptSock() blocks if there are
Xno connection requests pending on the server socket. If this command
Xfails for any reason, it returns SS_NULL.
X
Xint SockClose( SOCKET * sp)
X
XCloses the socket pointed to by 'sp', and performs some internal
Xhousecleaning.  Returns 0 if successful, otherwise returns SS_EOF.
X
X######## Commands for reading from and writing to sockets ########
XMost of these commands are intended to behave similarly to their stdio
Xnamesakes; replace FILE in your man pages with SOCKET and you'll
Xhave a pretty good description of what these routines do.  For example:
XSockPuts() behaves similarly to the stdio function fputs().
X
Xint SockGetc( SOCKET * sp )
X
XReturns the next character (byte) to be input from the socket 'sp'.
X
Xint SockPutc( char c, SOCKET * sp )
X
XWrites the character 'c' to the socket 'sp'.  Returns the character
Xwritten.
X
Xint SockWrites( char * string, SOCKET * sp )
Xint SockPuts( char * string, SOCKET * sp )
X
XThese functions write the string 'string' to the socket 'sp'.
XSockPuts returns the value of the last character written to the buffer
Xif successful, otherwise it returns SS_EOF. SockWrites returns 0 if
Xsuccessful, otherwise returns SS_EOF.  SockPuts buffers it's output,
Xwhile SockWrites issues the SockFlush() command on 'sp' to force
Xcharacters in 'string' to be sent.
X
Xchar * SockGets( char * buffer, int nbytes, SOCKET * sp )
X
XReads characters from the socket 'sp' into 'buffer' until nbytes have
Xbeen read, a newline character is read, or an end of file is reached.
XIf the end of file is reached SockGets returns SS_EOF, otherwise it
Xreturns 'buffer'.
X
Xint SockFlush( SOCKET * sp)
X
XForces any buffered data in 'sp' to be sent.  Returns 0 on success,
Xand SS_EOF on failure.
X
X################ Checking sockets ###############
XThese routines are built around the select() system call, and are
Xused to check for sockets being ready for reading and writing,
Xor to wait for an event to occur ( like the arival of data from
Xanother machine ).
X
Xint SockSelect( double timeval, char * flag )
X
XThis function performs a select() system call on all open sockets.
XSockSelect() returns the number of sockets which select() found to be
Xready.  To examine the state of a particular socket after calling
XSockSelect() you must use one of: IsReadSet(), IsWriteSet(), or
XIsExceptSet().  The select call will block for 'timeval' seconds if
Xtimeval is positive.  If 'timeval' is negative, then the select call
Xwill block indefinitely until at least one of the open sockets is
Xready.  The variable 'flag' is used to determine what the select()
Xwill check for:
X
X 	'flag'		select() checks for
X        -----           ------------------
X	"r"		socket ready to be read from.
X	"w"		socket ready to be written to.
X	"e"		socket has exceptional condition pending.
X
XAny combination of the set {r,w,e} may be used in any order.  For
Xexample: flag = "er", will cause select() to check for sockets ready
Xto be read from or having exceptional conditions pending.
X
Xint IsWriteSet( SOCKET * sp )
Xint IsReadSet( SOCKET * sp )
Xint IsExceptSet( SOCKET * sp )
X
XThese functions check a particular socket to see it is ready for
Xreading, writing, or has an exceptional condition pending.  If the
Xsocket is ready, the functions return 1, otherwise they return 0.
XThese functions use information obtained during the last SockSelect()
Xcall.
X
Xint SockIsRead( SOCKET * sp )
Xint SockIsWrite( SOCKET * sp )
X
XThese functions combine the select() system call and FD_ISSET() macro,
Xand are used to check whether a socket is ready for reading or
Xwriting.  If the socket is ready the functions return 1, otherwise
Xthey return 0.  These functions have no timeval parameter (see
XSockSelect()), and return immediately.
X
SHAR_EOF
chmod 0644 Notes || echo "restore of Notes fails"
sed 's/^X//' << 'SHAR_EOF' > Testing &&
XSeveral test programs have been provided which, in addition to making
Xsure the code works, serve as examples of how to use the library
Xroutines.
X
XThe test programs are set up to work in pairs; client1 works with
Xserver1, client2 works with server2 and so on.
X
XTesting is most easily done on a windowing system.  Open a window,
Xand start up a server program:
X% server1
XThen open another window, and start up a client program:
X% client1
X
XTest program descriptions:
X
Xclient1/server1 - server1 waits for a client1 to connect and prints
X	out any and all input it recieve from the client.  Once the
X	client disconnects the server waits for another connection.
X	Client1 connects to the server, and accepts input from the
X	standard input which it sends to the server. Upon receipt
X	of an EOF (^D) client1 stops and says goodbye.
X
Xclient2/server2 - server2 waits a short while for a client to connect.
X	If no client attempts a connection, the server goes off and
X	does something else, and then waits again.  If a client connects,
X	then server2 behaves the same as server1.  Client2 is identical
X	to client1.
X
Xclient3/server3 - Server3 is able to multiplex clients.  Whenever one
X	of the clients is ready for reading, the server reads a single
X	line, and then waits again.  Client3 requires no user input;
X	it carries on a trivial conversation with the server. I found
X	this command to be usefull for starting several client3 programs:
X	% sleep 6 ; ( client3 & client3 & client3 & )
X	The command waits six seconds ( giving me time to bring up the
X	window I was running the server in ), and then starts three client3
X	programs in parallel.
SHAR_EOF
chmod 0644 Testing || echo "restore of Testing fails"
sed 's/^X//' << 'SHAR_EOF' > Install &&
XTo build the Simple Socket Library:
X
X1. Edit the Makefile.
X
XYou will need to set the install directories, and uncomment the
Xdefines that go along with your machine.
X
X2. Edit test.h
X
XYou will need to set the machine name and port number that you
Xwant the test programs to use.
X
X3. type  % make
X
XTakes about 3 minutes on my sun3 to build the library and test
Xprograms.
X
X4. Run the test programs.  See the file named Testing for instructions
Xon how to do this.
X
X5. If your happy with what you see, then go ahead and install
Xthe library and header file.  Make sure that you create the directories
Xyou named at the top of the Makefile.
X
Xmake install
X
X6. Send problems, fixes, and comments to:  mat@zues.opt-sci.arizona.edu
X
XI can't promise to fix problems associated with porting to new machines,
Xas I've already ported the code to all the machines that I know anything
Xat all about.
X
X--Mat
SHAR_EOF
chmod 0644 Install || echo "restore of Install fails"
sed 's/^X//' << 'SHAR_EOF' > ss.c &&
X/* Simple Socket Libary
X   Written by Mat Watson and Hubert Bartels.  12/01/90
X*/
X/*
XCopyright (c) 1990 Mat Watson and Hubert Bartels. All rights reserved. 
X
XPermission to use, copy, and/or distribute for any purpose and 
Xwithout fee is hereby granted, provided that both the above copyright 
Xnotice and this permission notice appear in all copies and derived works. 
XFees for distribution of this software or for derived sources may only 
Xbe charged with the express written permission of the copyright holders. 
XThis software is provided ``as is'' without express or implied warranty.
X*/
X
X#include "ss.h"
X
X/* #include "private_globals.h" */
X/* #include <signal.h> */
X
Xint ss_server_flag;
Xint ss_client_flag;
Xint ss_init_flag=0;
XSOCKARRAY ss_sockbuff;
Xint max_sd;  /* this variable is a private global */
X
Xchar *malloc();
X
XSOCKET *AcceptSock( ssp )
XSOCKET *ssp;               /* Server Socket Pointer */
X{
X  SOCKET *csp;             /* Client Socket Pointer */
X  struct sockaddr *adrp;   /* Address Poiner        */
X  int *adrlen;             /* Address Length        */
X  int csd;                 /* Client Socket Descriptor */
X
X  if( !(ss_sockbuff.n_sockets < MaxSockets) )
X    return (SOCKET *) SS_NULL;
X
X  /* Allocate space for the client's socket structure */
X  csp = (SOCKET *)malloc( sizeof( SOCKET ));
X
X  if( !csp )
X    sserror("AcceptSock: malloc()",EXIT);
X
X  csp->_rcnt = 0;
X  csp->_rptr = SS_NULL;
X  csp->_rbase = SS_NULL;
X  csp->_wcnt = 0;
X  csp->_wptr = SS_NULL;
X  csp->_wbase = SS_NULL;
X  csp->_flag = 0;
X
X  adrp = (struct sockaddr *)0;
X  adrlen = (int *)0;
X
X  csd = accept( ssp->sd, adrp, adrlen );
X  if( csd < 0 ) {
X#ifdef Verbose_Errors
X    sserror("AcceptSock: accept()",CONT);
X#endif
X    return SS_NULL;
X  }
X
X  /* Set the value of the maximum socket descriptor used so far */
X  max_sd = csd >= max_sd ? csd : max_sd;
X  
X  csp->sd = csd;
X
X  AddSock( csp, &ss_sockbuff );
X
X  /* Return a pointer to the client socket */
X  return csp;
X}
X
Xvoid AddSock( sp, sap )
XSOCKET *sp;
XSOCKARRAY *sap;
X{
X  int n;
X  n = sap->n_sockets;
X  sap->n_sockets++;
X  sap->spa[n] = sp;
X  return;
X}
X
XSOCKET *ConnectSock( hostname, port_number )
Xchar *hostname;
Xunsigned port_number;
X{
X  SOCKET *sp;
X  struct hostent *hep;
X  struct sockaddr *adrp;
X  int adrlen;
X  int result;
X
X  SockInit( 'c' );
X
X  sp = (SOCKET *) malloc( sizeof( SOCKET ) );
X
X  if( !sp ) {
X#ifdef Verbose_Errors
X    sserror("ConnectSock: malloc()",CONT);
X#endif
X    return (SOCKET *) SS_NULL;
X  }
X
X  sp->_rcnt = 0;
X  sp->_rptr = SS_NULL;
X  sp->_rbase = SS_NULL;
X  sp->_wcnt = 0;
X  sp->_wptr = SS_NULL;
X  sp->_wbase = SS_NULL;
X  sp->_flag = 0;
X
X  AddSock( sp, &ss_sockbuff );
X
X  hep = gethostbyname( hostname );
X  if( hep == (struct hostent *)0 ) {
X#ifdef Verbose_Errors
X    perror("ConnectSock: gethostbyname()");
X#endif
X    return (SOCKET *) SS_NULL;
X  }
X
X  bcopy( (char *)(hep->h_addr), (char *)&(sp->addr.sin_addr), hep->h_length );
X
X  sp->addr.sin_family = AF_INET;
X  sp->addr.sin_port = htons( (u_short)port_number );
X  sp->sd = socket( AF_INET, SOCK_STREAM, 0 );
X  if( sp->sd < 0 ) {
X#ifdef Verbos_Errors
X    sserror("ConnectSock: socket()",CONT);
X#endif
X    return (SOCKET *) SS_NULL;
X  }
X
X  adrp = (struct sockaddr *) &(sp->addr);
X  adrlen = sizeof( sp->addr );
X
X  result = connect( sp->sd, adrp, adrlen );
X  if( result < 0 ) {
X#ifdef Verbos_Errors
X    sserror("ConnectSock: connect()",CONT);
X#endif
X    close(sp->sd);
X    free((char *) sp );
X    return (SOCKET *) SS_NULL;
X  }
X  max_sd = sp->sd >= max_sd ? sp->sd : max_sd ;
X  return sp;
X}
X
Xint IsExceptSet( sp )
XSOCKET *sp;
X{
X  int result, i;
X  SOCKARRAY * sap;
X  sap = &ss_sockbuff;
X  i = SockIndex( sp, sap );
X  if( i == -1 )
X    return -1;           /* Socket is not in ss_sockbuff */
X  result = FD_ISSET( sap->spa[i]->sd, &(sap->exceptfds) );
X  return result;
X}
X
Xint IsReadSet( sp )
XSOCKET *sp;
X{
X  int result, i;
X  SOCKARRAY * sap;
X  sap = &ss_sockbuff;
X  i = SockIndex( sp, sap );
X  if( i == -1 )
X    return i;               /* Socket is not in the ss_sockbuff */
X  result = FD_ISSET( sap->spa[i]->sd, &(sap->readfds) );
X  return result;
X}
X
Xint IsWriteSet( sp )
XSOCKET *sp;
X{
X  int result, i;
X  SOCKARRAY * sap;
X  sap = &ss_sockbuff;
X  i = SockIndex( sp, sap );
X  if( i == -1 )
X    return -1;    /* Socket is not in ss_sockbuff */
X  result = FD_ISSET( sap->spa[i]->sd, &(sap->writefds) );
X  return result;
X}
X
Xvoid RemoveSock( sp )
XSOCKET *sp;
X{
X  int i, n;
X  SOCKARRAY *sap;
X  sap = &ss_sockbuff;
X
X  for( i = 0; i < sap->n_sockets; i++ ) 
X    if( sp == sap->spa[i] ) break;
X
X  n = i;
X
X  for( i = n + 1; i < sap->n_sockets; i++ )
X    sap->spa[i-1] = sap->spa[i];
X
X  sap->spa[sap->n_sockets - 1] = SS_NULL;
X  sap->n_sockets--;
X  return;
X}
X
X
XSOCKET *ServerSock( port_number )
Xunsigned port_number;
X{
X  int sd;
X  int optval;
X  unsigned optlen;
X  SOCKET *sp;
X  int result;
X
X  SockInit( 's' );
X
X  sd = socket( AF_INET, SOCK_STREAM, 0 );
X  if( sd < 0 ) {
X    sserror("ServerSock: socket()",EXIT);
X    /* return (SOCKET *) SS_NULL; */
X  }
X  
X  optval = 1;  /* any nonzero value will cause the flag to be set */
X  optlen = sizeof( int );
X  result = setsockopt( sd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, optlen );
X  if( result != 0 ) {
X#ifdef Verbose_Errors
X    sserror("ServerSock: setsockopt()",CONT);
X#endif
X    return (SOCKET *) SS_NULL;
X  }
X
X  sp = (SOCKET *)malloc( sizeof(SOCKET));
X  if( !sp ) {
X#ifdef Verbose_Errors
X    sserror("ServerSock: mallock()",CONT);
X#endif
X    return (SOCKET *) SS_NULL;
X  }
X  
X  sp->addr.sin_family = AF_INET;
X  sp->addr.sin_addr.s_addr = INADDR_ANY;
X  sp->addr.sin_port = htons( (u_short)port_number );
X
X  result = bind( sd, (struct sockaddr *)&(sp->addr), sizeof(struct sockaddr));
X  if( result !=0 ) {
X#ifdef Verbose_Errors
X    sserror("ServerSock: bind()",CONT);
X#endif
X    return (SOCKET *) SS_NULL;
X  }
X
X  max_sd = sd >= max_sd ? sd : max_sd;
X  sp->sd = sd;
X  sp->_rcnt = 0;
X  sp->_rptr = SS_NULL;
X  sp->_rbase = SS_NULL;
X  sp->_wcnt = 0;
X  sp->_wptr = SS_NULL;
X  sp->_wbase = SS_NULL;
X  sp->_flag = 0;
X  
X  result = listen( sd, 5 );
X  if( result != 0 ) {
X#ifdef Verbose_Errors
X    sserror("ServerSock: listen()",CONT);
X#endif
X    return (SOCKET *) SS_NULL;
X  }
X
X  AddSock( sp, &ss_sockbuff );
X  
X  return sp;
X}
X
Xvoid SockArrayInit( sap )
XSOCKARRAY *sap;
X{
X  int i;
X  sap->n_sockets = 0;
X  for( i = 0; i < MaxSockets; i++ )
X    sap->spa[i] = SS_NULL;
X  return;
X}
X
X/*	fclose.c
X	
X	Hubert Bartels
X	
X	August 8, 1985
X	
X	fclose closes the file and frees the entry in the _iob block.
X
X        Modified for sockets by Mat Watson 12/1/90
X
X*/
Xint SockClose(sp)
XSOCKET *sp;
X{
X	int result;
X
X  RemoveSock( sp );
X
X  result = SockFlush(sp);      /* Flush the buffer if needed */
X  if( result < 0 ) {
X#ifdef Verbose_Errors
X    sserror("SockClose(): SockFlush failed",CONT);
X#endif
X    return SS_EOF;
X  }
X  result = close((int)sp->sd);		/* Close the file */
X  if( result < 0 )
X    sserror("SockClose: close() failed",EXIT);
X  sp->sd = '\0';
X  sp->_rcnt = 0;
X  sp->_rptr = SS_NULL;
X  if( (sp->_flag & SS_IONBF) == 0 ) 
X    free( (char *) sp->_rbase );
X  sp->_rbase = SS_NULL;
X
X  sp->_wcnt = 0;
X  sp->_wptr = SS_NULL;
X  if( (sp->_flag & SS_IONBF) == 0 ) 
X    free( (char *) sp->_wbase );
X  sp->_wbase = SS_NULL;
X  sp->_flag = 0;
X  return 0; /* Success */
X}
X
X/* SockFilbuf.c
X   Modified for sockets by Mat Watson 12/01/90
X*/
X/*	filbuf.c
X	
X	Hubert Bartels
X	
X	July 8, 1985
X	
X	filbuf is used to fill the buffer of files being read by the
X	polyp. As such, it is part of the file system of the polyp.
X*/
Xint _SockFilbuf(sp)
XSOCKET *sp;
X{
X  static char smallbuf[NOFILE];  /* for unbuffered i/o */
X  char *calloc();
X
X  if( (sp->_flag & (SS_IOEOF | SS_IOERR)) != 0 )
X    return(SS_EOF);
X
X  while (sp->_rbase == SS_NULL) /* find buffer space */
X    if(sp->_flag & SS_IONBF) /* unbuffered */
X      sp->_rbase = &smallbuf[sp->sd];
X    else if ((sp->_rbase = calloc(SS_BUFSIZ, 1)) == SS_NULL)
X      sp->_flag |= SS_IONBF;	/* Cannot get unbuffered */
X    else
X      sp->_flag |= SS_IOMYBUF;	/* Got a big buffer */
X
X  sp->_rptr = sp->_rbase;
X  sp->_rcnt = read(sp->sd, sp->_rptr,
X		  sp->_flag & SS_IONBF ? 1 : SS_BUFSIZ);
X  if(--sp->_rcnt < 0) {
X    if(sp->_rcnt == -1 || errno == ECONNRESET ) /* ECONNRESET if socket */
X      sp->_flag |= SS_IOEOF;                    /* was closed.          */
X    else {
X      sp->_flag |= SS_IOERR;
X#ifdef Verbose_Errors
X      sserror("SockFilbuf(): read()",CONT);
X#endif
X    }
X    sp->_rcnt = 0;
X    return(SS_EOF);
X  }
X  return(*sp->_rptr++ & 0377);	/* make character positive */
X}
X/* SockFlsbuf.c
X   Modified for sockets by Mat Watson 12/01/90
X*/
X/*	flsbuf.c
X	
X	Hubert Bartels
X	
X	July 29, 1985
X	
X	Version 2.0
X			Correct the bug, initialization of buffer
X	flsbuf is used to flush the buffer of files being written by
X	the polyp. As such, it is part of the file system of the polyp.
X	_flsbuf may be called with unbuffered or buffered data. There
X	may be data still in the buffers when the program completes. 
X	either fflush or fclose should be called when closing the program
X	or file, to enure that all the data has been read out.
X*/
X
Xint _SockFlsbuf(x,sp)
Xchar x;
XSOCKET *sp;
X{
X	static char smallbuf[NOFILE];		/* for unbuffered i/o */
X	char *malloc();
X
X	if( (sp->_flag & SS_IOERR) != 0)
X		return SS_EOF;
X	if (sp->_wbase == SS_NULL) { /* find buffer space */
X		if(sp->_flag & SS_IONBF) { /* unbuffered */
X			sp->_wbase = &smallbuf[sp->sd];
X		}
X		else if ((sp->_wbase = malloc(SS_BUFSIZ)) == SS_NULL) {
X			sp->_wbase = &smallbuf[sp->sd];
X			sp->_flag |= SS_IONBF;	/* Cannot get unbuffered */
X		}
X		else
X			sp->_flag |= SS_IOMYBUF;	/* Got a big buffer */
X		sp->_wptr = sp->_wbase;		/* Initialize pointer */
X		sp->_wcnt = (sp->_flag & SS_IONBF)?0:SS_BUFSIZ; /* Buffer size */
X		if(sp->_flag & SS_IONBF) {		/* If unbuffered */
X			write(sp->sd,&x, 1);
X			sp->_wcnt = 0;
X			}
X		else {
X		        *(sp)->_wptr++ = x;		/* Store char */
X		        sp->_wcnt--;
X		        }
X		return 0;
X	} /* End of sp->_base buffer allocation */
X	if(sp->_flag & SS_IONBF) {		/* If unbuffered */
X		write(sp->sd,&x, 1);
X		sp->_wcnt = 0;
X	} else {
X		if(write(sp->sd,sp->_wbase,(SS_BUFSIZ-sp->_wcnt)) == -1)
X			sp->_flag |= SS_IOERR;
X		sp->_wcnt = SS_BUFSIZ;
X		sp->_wptr = sp->_wbase;
X		*(sp)->_wptr++ = x;
X		sp->_wcnt--;
X	}
X	return 0;
X}
X
X/*  SockFlush.c
X   Modified for sockets by Mat Watson 12/01/90
X*/
X/*	fflush.c
X	
X	Hubert Bartels
X	
X	July 29, 1985
X	
X	fflush is called to clear out the buffers to ensure that everything
X	has been written. Called in the middle of the programs, it ensures
X	that the information will be complete before finishing some long
X	calculation. Called at the end, it performs the sync function for
X	the program.
X*/
X
Xint SockFlush(sp)
XSOCKET *sp;
X{
X  int n_writen;
X  char *my_base;
X
X  if(
X     /* (sp->_flag &SS_IOWRT) == 0  || */ /* Not Appropriate for Sock */
X     (sp->_flag & SS_IOERR) != 0
X     ) {
X#ifdef Verbose_Errors
X    sserror("SockFlush: SS_IOERR is set",CONT);
X#endif
X    return SS_EOF;				/* If errors in file */
X  }
X  if( sp->_wbase == SS_NULL )
X    return 0;                                /* No buffer to flush */
X  if((sp->_flag & SS_IONBF) == 0) {		/* If buffered */
X    my_base = sp->_wbase;
X    while( sp->_wcnt != SS_BUFSIZ ) {
X      n_writen = write( sp->sd, my_base, (SS_BUFSIZ - sp->_wcnt) );
X      if( n_writen < 0 ) {
X        extern int errno;
X        if( errno == EPIPE )
X          return 0;
X#ifdef Verbose_Errors
X	sserror("SockFlush: write error",CONT);
X	/* When the socket has been set non blocking and the socket
X	   would otherwise block, write will return a -1 and errno
X	   will be set to EWOULDBLOCK. */
X	/* When write tries to send characters to a client or server
X	   that has dissconnected errno is set to EPIPE */
X#endif
X        return SS_EOF;
X      }
X      sp->_wcnt += n_writen;
X      my_base += n_writen;
X    }
X    sp->_wcnt = SS_BUFSIZ;
X    sp->_wptr = sp->_wbase;
X  }
X  return 0;
X}
X
X/* SockGets.c
X   Modified for sockets by Mat Watson 12/01/90
X*/
X/*	fgets.c
X
X	read a newline-terminated string from device (file) dev
X
X	Hubert Bartels
X	August 12, 1985
X*/
X
Xchar *SockGets(s, n, sp)
Xchar *s;
Xint n;
XSOCKET *sp;
X{
X	int c;
X	char *cs;
X
X	cs = s;
X        while ( --n > 0 && ( c = SockGetc( sp )) >= 0 ) {
X		*cs++ = (char)c;
X		if ( (char)c =='\n' )
X			break;
X	}
X	if ( c < 0 && cs == s )
X		return SS_NULL;
X	*cs++ = '\0';
X	return s;
X}
X
X/* SockIndex.c
X   Writen by Mat Watson 12/01/90
X*/
X
Xint SockIndex( sp, sap )
XSOCKET *sp;
XSOCKARRAY *sap;
X{
X  int i;
X
X  for( i = 0; i < sap->n_sockets; i++ ) 
X    if( sp == sap->spa[i] ) break;
X
X  if( i == sap->n_sockets )
X    i = -1;
X
X  return i;
X}
X
X/* SockInit.c
X   Writen by Mat Watson 12/01/90
X*/
Xvoid SockInit( c )
Xchar c;
X{
X  if( ss_server_flag == 0 && c == 's' )
X      ss_server_flag = 1;
X
X  if( ss_client_flag == 0 && c == 'c' )
X      ss_client_flag = 1;
X
X  if( ss_init_flag == 0 ) {
X    ss_sockbuff.n_sockets = 0;
X    ss_init_flag = 1;
X    max_sd = 0;
X    signal(SIGPIPE,SockSignal);
X  }
X
X  return;
X}
X
X/* SockIsRead.c
X   Writen by Mat Watson 12/01/90
X*/
X
Xint SockIsRead( sp )
XSOCKET *sp;
X{
X  int result;
X  struct timeval to;
X  fd_set readfds;
X
X  to.tv_sec = 0;
X  to.tv_usec = 0;
X  
X
X  FD_ZERO( &readfds );
X  FD_SET( sp->sd, &readfds );
X  result = select( max_sd+1, &readfds, (fd_set *)0, (fd_set *)0, &to );
X  if( result == -1 )
X    sserror("SockSelect1(): select() returned -1",CONT);
X
X  return FD_ISSET( sp->sd, &readfds ) ? 1 : 0;
X}
X
Xint SockIsWrite( sp )
XSOCKET *sp;
X{
X  int result;
X  struct timeval to;
X  fd_set writefds;
X
X  to.tv_sec = 0;
X  to.tv_usec = 0;
X  
X
X  FD_ZERO( &writefds );
X  FD_SET( sp->sd, &writefds );
X  result = select( max_sd+1, (fd_set *)0, &writefds, (fd_set *)0, &to );
X  if( result == -1 )
X    sserror("SockSelect1(): select() returned -1",CONT);
X
X  return FD_ISSET( sp->sd, &writefds ) ? 1 : 0;
X}
X
X/*	SockPuts.c
X	Modified for sockets by Mat Watson 12/01/90
X*/
X/*	fputs.c
X
X	places a string on the output file pointed to by fp.
X
X	Hubert Bartels
X	August 12, 1985
X*/
Xint SockPuts(s, sp)
XSOCKET *sp;
Xchar *s;
X{
X	int r;
X	char c;
X
X	while (c = *s++)
X                r = SockPutc(c, sp);
X	return(r);
X}
X
Xint SockSelect( timeout, flag )
Xdouble timeout;
Xchar *flag;
X{
X  int i;
X  int result;
X  long seconds;
X  long microseconds;
X  SOCKARRAY * sap;
X  struct timeval to, *tvp;
X  fd_set *rfdsp, *wfdsp, *efdsp;
X  char *cp;
X
X  sap = &ss_sockbuff;
X
X  if( timeout < 0.0 ) {
X    tvp = SS_NULL;
X  }
X  else {
X    seconds = timeout / 1.0;
X    microseconds = (timeout - (double)seconds) / 1e-6;
X    to.tv_sec = seconds;
X    to.tv_usec = microseconds;
X    tvp = &to;
X  }
X
X  rfdsp = (fd_set *)0;
X  wfdsp = (fd_set *)0;
X  efdsp = (fd_set *)0;
X
X  for( cp = flag; *cp != '\0'; cp++ ) {
X    if( *cp == 'r' ) {
X      rfdsp = &(sap->readfds);
X      FD_ZERO( rfdsp );
X      for( i = 0; i < sap->n_sockets; i ++ )
X	FD_SET( sap->spa[i]->sd, rfdsp );
X    } 
X    else if( *cp == 'w' ) {
X      wfdsp = &(sap->writefds);
X      FD_ZERO( wfdsp );
X      for( i = 0; i < sap->n_sockets; i ++ )
X	FD_SET( sap->spa[i]->sd, wfdsp );
X    }
X    else if( *cp == 'e' ) {
X      efdsp = &(sap->exceptfds);
X      FD_ZERO( efdsp );
X      for( i = 0; i < sap->n_sockets; i ++ )
X	FD_SET( sap->spa[i]->sd, efdsp );
X    }
X    else {
X      sserror("SockSelect(): Unknown flag",CONT);
X    }
X  }
X
X  result = select( max_sd+1, rfdsp, wfdsp, efdsp, tvp );
X  if( result == -1 )
X    perror("SockSelect(): select() returned -1");
X
X  return result;
X}
X
Xvoid SockSignal( sig )
Xint sig;
X{
X  switch ( sig )
X    {
X    case SIGPIPE :
X#ifdef Verbose_Signals
X      sserror(
X        "Caught a SIGPIPE signal, Server/Client probably disconnected",
X	 CONT );
X#endif
X      break;
X    }
X  return;
X}
X
X/* SockWrites
X   Writen by Mat Watson 12/01/90
X   Based on fputs.c by Hubert Bartels.
X*/
Xint SockWrites(s, sp)
XSOCKET *sp;
Xchar *s;
X{
X	int r;
X	char c;
X
X	while (c = *s++)
X                r = SockPutc(c, sp);
X	r = SockFlush( sp );
X	return(r);
X}
X
Xvoid sserror( mesg, code )
Xchar * mesg;
Xint code;
X{
X  fprintf(stderr,"%s",mesg);
X  fputc(' ',stderr);
X  fflush(stderr);
X  perror(NULL);
X  if ( code == EXIT ) exit( 1 );
X  return;
X}
SHAR_EOF
chmod 0644 ss.c || echo "restore of ss.c fails"
sed 's/^X//' << 'SHAR_EOF' > ss.base.h &&
X/* ss.h */
X/* writen by Mat Watson 12/01/90 */
X/*
XCopyright (c) 1990 Mat Watson and Hubert Bartels. All rights reserved. 
X
XPermission to use, copy, and/or distribute for any purpose and 
Xwithout fee is hereby granted, provided that both the above copyright 
Xnotice and this permission notice appear in all copies and derived works. 
XFees for distribution of this software or for derived sources may only 
Xbe charged with the express written permission of the copyright holders. 
XThis software is provided ``as is'' without express or implied warranty.
X*/
X
X#define MaxSockets 30
X#define EXIT 0
X#define CONT 1
X
Xtypedef struct m_in_socket SOCKET;
Xtypedef struct m_in_socket_array SOCKARRAY;
X
Xstruct m_in_socket {
X  int sd;
X  struct sockaddr_in addr;
X  int _rcnt;
X  char *_rptr;
X  char *_rbase;
X  int _rbufsiz;
X  int _wcnt;
X  char *_wptr;
X  char *_wbase;
X  int _wbufsiz;
X  short _flag;
X};
X
Xstruct m_in_socket_array {
X  int n_sockets;
X  SOCKET *spa[MaxSockets];
X  fd_set readfds;
X  fd_set writefds;
X  fd_set exceptfds;
X};
X
X/* define flags */
X/**/
X#define SS_IOFBF  0
X#define SS_IOREAD 01
X#define SS_IOWRT  02
X#define SS_IONBF  04
X#define SS_IOMYBUF        010
X#define SS_IOEOF  020
X#define SS_IOERR  040
X#define SS_IOSTRG 0100
X#define SS_IOLBF  0200
X#define SS_IORW   0400
X#define SS_NULL    0
X#define SS_EOF     (-1)
X#define SS_BUFSIZ  1024
X
X#define SockGetc(p)  (--(p)->_rcnt>=0? ((int)*(p)->_rptr++):_SockFilbuf(p))
X
X#define SockPutc(x, p)  (--(p)->_wcnt >= 0 ?\
X	    (int)(*(p)->_wptr++ = (char)(x)) :\
X	       (((p)->_flag & SS_IOLBF) && -(p)->_wcnt < (p)->_wbufsiz ?\
X                ((*(p)->_wptr = (char)(x)) != '\n' ?\
X                        (int)(*(p)->_wptr++) :\
X                        _SockFlsbuf(*(char *)(p)->_wptr, p)) :\
X                _SockFlsbuf((char)(x), p)))
X
X#define SockEof(p)         (((p)->_flag&SS_IOEOF)!=0)
X#define SockError(p)       (((p)->_flag&SS_IOERR)!=0)
X#define SockFileno(p)       ((p)->sd)
X#define SockClearerr(p)     (void) ((p)->_flag &= ~(SS_IOERR|SS_IOEOF))
X
X#ifdef __STDC__
X# define	P_(s) s
X#else
X# define P_(s) ()
X#endif
X
X
X/* ss.c */
XSOCKET *AcceptSock P_((SOCKET *ssp ));
Xvoid AddSock P_((SOCKET *sp , SOCKARRAY *sap ));
XSOCKET *ConnectSock P_((char *hostname , unsigned port_number ));
Xint IsExceptSet P_((SOCKET *sp ));
Xint IsReadSet P_((SOCKET *sp ));
Xint IsWriteSet P_((SOCKET *sp ));
Xvoid RemoveSock P_((SOCKET *sp ));
XSOCKET *ServerSock P_((unsigned port_number ));
Xvoid SockArrayInit P_((SOCKARRAY *sap ));
Xint SockClose P_((SOCKET *sp ));
Xint _SockFilbuf P_((SOCKET *sp ));
Xint _SockFlsbuf P_((int x , SOCKET *sp ));
Xint SockFlush P_((SOCKET *sp ));
Xchar *SockGets P_((char *s , int n , SOCKET *sp ));
Xint SockIndex P_((SOCKET *sp , SOCKARRAY *sap ));
Xvoid SockInit P_((int c ));
Xint SockIsRead P_((SOCKET *sp ));
Xint SockIsWrite P_((SOCKET *sp ));
Xint SockPuts P_((char *s , SOCKET *sp ));
Xint SockSelect P_((double timeout , char *flag ));
Xvoid SockSignal P_((int sig ));
Xint SockWrites P_((char *s , SOCKET *sp ));
Xvoid sserror P_((char *mesg , int code ));
X
X#undef P_
SHAR_EOF
chmod 0644 ss.base.h || echo "restore of ss.base.h fails"
sed 's/^X//' << 'SHAR_EOF' > ss.sun.h &&
X/* ss.h */
X/* writen by Mat Watson 12/01/90 */
X/*
XCopyright (c) 1990 Mat Watson and Hubert Bartels. All rights reserved. 
X
XPermission to use, copy, and/or distribute for any purpose and 
Xwithout fee is hereby granted, provided that both the above copyright 
Xnotice and this permission notice appear in all copies and derived works. 
XFees for distribution of this software or for derived sources may only 
Xbe charged with the express written permission of the copyright holders. 
XThis software is provided ``as is'' without express or implied warranty.
X*/
X#include <stdio.h>
X#include <signal.h>
X#include <errno.h>
X#include "stdlib.h"
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <sys/time.h>
SHAR_EOF
chmod 0644 ss.sun.h || echo "restore of ss.sun.h fails"
sed 's/^X//' << 'SHAR_EOF' > ss.ibm.h &&
X/* ss.h */
X/* writen by Mat Watson 12/01/90 */
X/*
XCopyright (c) 1990 Mat Watson and Hubert Bartels. All rights reserved. 
X
XPermission to use, copy, and/or distribute for any purpose and 
Xwithout fee is hereby granted, provided that both the above copyright 
Xnotice and this permission notice appear in all copies and derived works. 
XFees for distribution of this software or for derived sources may only 
Xbe charged with the express written permission of the copyright holders. 
XThis software is provided ``as is'' without express or implied warranty.
X*/
X#include <stdio.h>
X#include <signal.h>
X#include <errno.h>
X#include "stdlib.h"
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <sys/select.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <sys/time.h>
SHAR_EOF
chmod 0644 ss.ibm.h || echo "restore of ss.ibm.h fails"
sed 's/^X//' << 'SHAR_EOF' > ss.3b2.h &&
X/* ss.h */
X/* writen by Mat Watson 12/01/90 */
X/*
XCopyright (c) 1990 Mat Watson and Hubert Bartels. All rights reserved. 
X
XPermission to use, copy, and/or distribute for any purpose and 
Xwithout fee is hereby granted, provided that both the above copyright 
Xnotice and this permission notice appear in all copies and derived works. 
XFees for distribution of this software or for derived sources may only 
Xbe charged with the express written permission of the copyright holders. 
XThis software is provided ``as is'' without express or implied warranty.
X*/
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <sys/in.h>
X#include <netdb.h>
X#include <sys/time.h>
Xtypedef int fd_set;
Xtypedef unsigned short u_short;
SHAR_EOF
chmod 0644 ss.3b2.h || echo "restore of ss.3b2.h fails"
sed 's/^X//' << 'SHAR_EOF' > test.h &&
X#define SERVER_HOSTNAME "zeus.opt-sci.arizona.edu" /* your machine name. */
X#define PORT_NUMBER 4020
SHAR_EOF
chmod 0644 test.h || echo "restore of test.h fails"
sed 's/^X//' << 'SHAR_EOF' > stdlib.h &&
X/* stdlib.h */
Xchar *malloc();
Xchar *calloc();
SHAR_EOF
chmod 0644 stdlib.h || echo "restore of stdlib.h fails"
sed 's/^X//' << 'SHAR_EOF' > server1.c &&
X/* server1.c */
X/*
X  Writen by Mat Watson 12/01/90
X*/
X/*
XCopyright (c) 1990 Mat Watson and Hubert Bartels. All rights reserved. 
X
XPermission to use, copy, and/or distribute for any purpose and 
Xwithout fee is hereby granted, provided that both the above copyright 
Xnotice and this permission notice appear in all copies and derived works. 
XFees for distribution of this software or for derived sources may only 
Xbe charged with the express written permission of the copyright holders. 
XThis software is provided ``as is'' without express or implied warranty.
X*/
X#include <stdio.h>
X#include "ss.h"
X#include "test.h"
X
Xmain()
X{
X  SOCKET *ssp, *csp;
X  char buffer[100], *result;
X  int n_read;
X
X  /* open a socket that clients can connect to. */
X  ssp = ServerSock( PORT_NUMBER );
X
X  /* Wait for a client to connect.  Accept input from the client.
X     Close the client socket pointer when the connection is broken
X     by the client.  Then wait again for another connection.  */
X
X  (void)printf("Use ^C to stop this program\n");
X
X  while(1) {
X    /* Wait for a client to connect. */
X    csp = AcceptSock( ssp );
X
X    (void)printf("Accepted a client.\n");
X
X    /* Read whatever the client sends, and print that out to the
X     * standard output.
X     */
X    while((result=SockGets(buffer,sizeof(buffer)-1,csp)) != SS_NULL) {
X      (void)printf("%s", buffer);
X    }
X
X    /* Close the socket. */
X    SockClose( csp );
X    (void)printf("Client disconnected.\n");
X  }
X}
SHAR_EOF
chmod 0644 server1.c || echo "restore of server1.c fails"
sed 's/^X//' << 'SHAR_EOF' > client1.c &&
X/* client1.c */
X/*
X  Writen by Mat Watson 12/01/90
X*/
X/*
XCopyright (c) 1990 Mat Watson and Hubert Bartels. All rights reserved. 
X
XPermission to use, copy, and/or distribute for any purpose and 
Xwithout fee is hereby granted, provided that both the above copyright 
Xnotice and this permission notice appear in all copies and derived works. 
XFees for distribution of this software or for derived sources may only 
Xbe charged with the express written permission of the copyright holders. 
XThis software is provided ``as is'' without express or implied warranty.
X*/
X#include <stdio.h>
X#include "ss.h"
X#include "test.h"
X
Xmain()
X{
X  char *line, buffer[200];
X  SOCKET *ssp;
X
X
X  /* Establish a connection with the server. */
X  ssp = ConnectSock( SERVER_HOSTNAME, PORT_NUMBER );
X  if( ssp == SS_NULL )
X    sserror("server1: ConnectSock()", EXIT);
X
X  /* Get text from the standard input, and send it to the server program */
X  (void)printf("enter text (^d to quit)\n");
X  while( ( line = gets(buffer) ) != NULL ) {
X    sprintf(buffer, "%s\n", line);
X    SockWrites(buffer, ssp);
X  }
X  (void)printf("Saioonara\n");
X  SockClose(ssp);
X  exit(0);
X}
SHAR_EOF
chmod 0644 client1.c || echo "restore of client1.c fails"
sed 's/^X//' << 'SHAR_EOF' > server2.c &&
X/* server2.c */
X/*
X  Writen by Mat Watson 12/01/90
X*/
X/*
XCopyright (c) 1990 Mat Watson and Hubert Bartels. All rights reserved. 
X
XPermission to use, copy, and/or distribute for any purpose and 
Xwithout fee is hereby granted, provided that both the above copyright 
Xnotice and this permission notice appear in all copies and derived works. 
XFees for distribution of this software or for derived sources may only 
Xbe charged with the express written permission of the copyright holders. 
XThis software is provided ``as is'' without express or implied warranty.
X*/
X#include <stdio.h>
X#include "ss.h"
X
X#define PORT_NUMBER 4020
X
Xmain()
X{
X  SOCKET *ssp, *csp;
X  char buffer[100], *result;
X
X  /* Open a socket for clients to connect to. */
X  ssp = ServerSock( PORT_NUMBER );
X
X  /* Look to see if a client wants to connect to the server socket.
X     If there's a client take input from it until the client breaks
X     the connection.  If there's no client trying to connect then
X     just do something else.                                        */
X
X  (void)printf("Use ^C to stop this program\n");
X
X  while(1) {
X    SockSelect( 5.0, "r" );       /* Wait 5 seconds for a client to
X				     request a connection. */
X    if( IsReadSet( ssp ) ) {      /* Is a client is trying to connect? */
X      csp = AcceptSock( ssp );    /* Accept the connection. */
X      (void)printf("Accepted a client\n");
X      /* Accept input until client disconnects. Print that output to the
X	 standard ouput. */
X      while( (result=SockGets(buffer,sizeof(buffer)-1,csp)) != SS_NULL ) {
X	(void)printf("%s", buffer);
X      }
X      if( SockClose(csp) == SS_EOF )
X	sserror("server2 - SockClose()",CONT);
X      (void)printf("Client disconnected.\n");
X    }
X    else {
X      (void)printf("doing something else\n");
X    }
X  }
X}
SHAR_EOF
chmod 0644 server2.c || echo "restore of server2.c fails"
sed 's/^X//' << 'SHAR_EOF' > client2.c &&
X/* client2.c */
X/*
X  Writen by Mat Watson 12/01/90
X*/
X/*
XCopyright (c) 1990 Mat Watson and Hubert Bartels. All rights reserved. 
X
XPermission to use, copy, and/or distribute for any purpose and 
Xwithout fee is hereby granted, provided that both the above copyright 
Xnotice and this permission notice appear in all copies and derived works. 
XFees for distribution of this software or for derived sources may only 
Xbe charged with the express written permission of the copyright holders. 
XThis software is provided ``as is'' without express or implied warranty.
X*/
X/* Exercise the SockPuts() or the SockWrites() command */
X#include <stdio.h>
X#include "ss.h"
X#include "test.h"
X
Xmain()
X{
X  char *line, buffer[200], message[200];
X  SOCKET *ssp;
X  ssp = ConnectSock( "zeus.opt-sci.arizona.edu", 4020 );
X  if( ssp == SS_NULL ) {
X    printf("ConnectSock returned SS_NULL.  Exiting ..... \n\n");
X    exit(1);
X  }
X
X#ifdef PUTC
X  SockPutc('a',ssp);
X  SockPutc('\n',ssp);
X  SockFlush(ssp);
X#endif /* PUTC */
X  SockWrites("I'm the client\n",ssp);
X  printf("enter text (^d to end)\n");
X  while( ( line = gets(buffer) ) != NULL ) {
X    sprintf(message,"%s\n",line);
X    /*
X    SockPuts(message,ssp);
X    */
X    SockWrites(message,ssp);
X  }
X  
X  SockFlush(ssp);
X  SockClose(ssp);
X  exit(0);
X}
SHAR_EOF
chmod 0644 client2.c || echo "restore of client2.c fails"
sed 's/^X//' << 'SHAR_EOF' > server3.c &&
X/* server.c */
X/* Writen by Mat Watson 12/01/90
X*/
X/*
XCopyright (c) 1990 Mat Watson and Hubert Bartels. All rights reserved. 
X
XPermission to use, copy, and/or distribute for any purpose and 
Xwithout fee is hereby granted, provided that both the above copyright 
Xnotice and this permission notice appear in all copies and derived works. 
XFees for distribution of this software or for derived sources may only 
Xbe charged with the express written permission of the copyright holders. 
XThis software is provided ``as is'' without express or implied warranty.
X*/
X#include <stdio.h>
X#include "ss.h"
X#include "test.h"
X
Xtypedef struct client_list CLIST;
Xchar *malloc();
Xstruct client_list {
X  CLIST *prev;
X  CLIST *next;
X  SOCKET *sp;
X};
X
XCLIST * AddClient( sp, clp )
XSOCKET *sp;
XCLIST *clp;
X{
X  CLIST *newclp;
X
X  newclp = (CLIST *)malloc( sizeof( struct client_list ) );
X  newclp->prev = SS_NULL;
X  newclp->sp = sp;
X  newclp->next = clp;
X  if( clp != SS_NULL )
X    clp->prev = newclp;
X  return newclp;
X}
X
XCLIST * RemoveClient( p_remove, clp )
XCLIST *p_remove;
XCLIST *clp;
X{
X  CLIST *p, *pnext;
X
X  for( p = clp; p; p = p->next )
X    if( p == p_remove )
X      break;
X  if( !p )
X    perror("RemoveClient(): sp not in client list");
X  if( p->prev )
X    (p->prev)->next = p->next;
X  if( p->next )
X    (p->next)->prev = p->prev;
X
X  pnext = p->next; /* we may want to return this value
X		      after deallocating p              */
X
X  free( (char *)p );
X  return p == clp ? pnext : clp;
X}
X
Xmain()
X{
X  SOCKET *ssp, *csp;
X  char buffer[100], *result, cmesg[100];
X  int n_read;
X  CLIST *clp, *p;
X  int test;
X
X  clp = SS_NULL;
X
X  (void)printf("Use ^C to stop this program.\n");
X
X  ssp = ServerSock( PORT_NUMBER );
X  printf("Server socket opened on descriptor %d\n",ssp->sd);
X
X  while(1) {
X    (void)printf("Waiting for input or a connection.\n");
X    SockSelect( -5.0, "r" );  /* Block until something is ready to be
X				 read from. */
X    if( IsReadSet( ssp ) > 0 ) { /* Connect new clients first. */
X      csp = AcceptSock( ssp );
X      clp = AddClient( csp, clp );
X      (void)printf("Adding client on descriptor %d.\n",csp->sd);
X      /* Ask a silly question.*/
X      SockWrites("Client, what is your pid number?\n",csp); 
X    }
X    else /* no new clients to connect. */ {
X      if( clp != SS_NULL ) {
X	(void)printf("Looping over clients.\n");
X	p = clp;
X	while( p ) {
X          (void)printf("  ");  /* Not necessary. Makes output look nice. */
X	  if( /* Either of the following two statements will work. */ 
X	     IsReadSet(p->sp) > 0 /* This function relies on the last call to
X				     SockSelect(). */
X	     /* SockIsRead(p->sp) /* This function calls select() again; however,
X		it only affects IsXxxSet() for this socket.
X		*/ 
X	     ) {
X	    result = SockGets(buffer, 99 , p->sp );
X	    if( result == SS_NULL ) {
X	      goto remove;
X	    }
X	    printf("%s",buffer); /* Get a silly answer. */
X	    /* Repeat the silly question. */
X	    test = SockWrites("Client, what is your pid number?\n",p->sp);
X	    if( test == SS_EOF ) {
X	      goto remove;
X	    }
X	  } /* end if socket ready to read from */
X	  else {
X	    (void)printf("Descriptor %d was not ready for reading.\n",p->sp->sd);
X	  }
X	  p = p->next;
X	  continue;
X	remove:  {
X	  CLIST *tmp;
X	  (void)printf("Removing client with descriptor = %d.\n",p->sp->sd);
X	  tmp = p->next;
X	  SockClose( p->sp );
X	  /* RemoveClient frees the space pointed to by p, so we need to
X	     store its 'next' pointer in the variable tmp. */
X	  clp = RemoveClient( p, clp );
X	  p = tmp;
X	} /* end ther remove clause. */
X	} /* end the while clients. */
X	(void)printf("Done looping over clients.\n");
X      } /* end if client list is non null. */
X    } /* end of else.  No new clients to connect. */
X  } /* end of infinite while loop. */
X}  /* end the program */
SHAR_EOF
chmod 0644 server3.c || echo "restore of server3.c fails"
sed 's/^X//' << 'SHAR_EOF' > client3.c &&
X/* client3.c */
X/*
X  Writen by Mat Watson 12/01/90
X*/
X#include <stdio.h>
X#include <errno.h>
X#include "ss.h"
X#include "test.h"
Xmain()
X{
X  char *line, buffer[200], message[200],message1[100];
X  int pid;
X  int result;
X  SOCKET *ssp;
X  int n;
X  int i;
X
X  /* Attempt to connect to the server program. */
X  ssp = ConnectSock( SERVER_HOSTNAME, PORT_NUMBER );
X  if( ssp == SS_NULL ) {
X    printf("ConnectSock returned SS_NULL.  Exiting ..... \n\n");
X    return;
X  }
X 
X  pid = getpid();
X  (void)printf("This clients pid is %d.\n",pid);
X  /* Create a message to send to the server program. */
X  sprintf(message,"Client pid = %d.\n",pid);
X
X  i = 3;
X  while( i ) {
X    i--;
X    sleep(2);
X    n = SockSelect( -5.0, "r" );  /* Block, Check for any open socket
X				      being read ready. */
X    if( IsReadSet( ssp ) ) {   /* Do we have anything to be read from the
X				  server? */
X      SockGets(message1,99,ssp);  /* Read it. */
X      printf("%s",message1);      /* Print it out. */
X      fflush(stdout);             /* Now! :-) */
X      result = SockWrites(message,ssp);
X      if( result == SS_EOF ) break; /* Probably connection broken. */
X    }
X  }
X  if( errno == EPIPE )
X    (void)printf("Server disconnected\n");
X  (void)printf("bye\n"); fflush(stdout);
X  exit(0);
X}
SHAR_EOF
chmod 0644 client3.c || echo "restore of client3.c fails"
exit 0