/*
 * tcpserver.c
 *
 * Example BSD TCP socket server. 
 * Paired with tcpclient.c.
 *
 * OS: SunOS
 * compiler: cc
 *	% cc -o tcpserver -g tcpserver.c
 *
 * To run:
 *	% tcpserver&
 *	% tcpclient localhost 
 *
 * The server listen on a #define hardwired port TCPPORT (see below).
 * It accepts some number of requests from the client and turns around
 * and writes those buffers back to the client.
 *
 * This is a simple test server.  A more 'normal' server would start
 * at boot and block in an accept call (or be coded for the UNIX inetd
 * which is in turn different).  After the accept call, the server would
 * fork a child to handle the connection.   This server simply handles
 * one connection and exits.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>

#define TRUE 1
#define FALSE 0

#define TCPPORT 10000		/* server port value */
#define BUFSIZE 1024		/* size of i/o buffer */
#define NOREADS 10		/* number of buffers transferred */

main(argc,argv)
char **argv;
{
	int sock;
	int size;
	char buf[BUFSIZE];
	int msgsock;
	struct sockaddr_in gotcha;
	int rc;
	int i;

	sock = initSocket();

	/* tcp starts listening for connections
	*/
	rc = listen(sock,5);
	if ( rc < 0) {
		perror("listen");
		exit(1);
	}

	/* accept one connection, will block here.
	*/
	size = sizeof (struct sockaddr_in);

	/* gotcha and size are returned when the connection comes in.
  	 * When the client calls connect, and a connection occurs, accept
 	 * returns.  gotcha holds the client ip address and client port value.
	 * NOTE: the size parameter is a *pointer*, not an integer.  The
	 * kernel returns the size of the socket structure in gotcha.
	 * This is called: "call by value-result". You have to pass in
	 * the size of the structure and the kernel returns the true result.
	 * For tcp/ip sockets the size never changes.  For unix sockets
	 * it may change.
	 */
	msgsock = accept(sock, &gotcha, &size);	

	if (msgsock < 0 ) {
		perror("accept");
		exit(1);
	}

	/* read and echo so many packets
	*/
	for ( i = 0; i < NOREADS; i++) {
		doRead(msgsock, buf, BUFSIZE);
		rc = write(msgsock, buf, BUFSIZE); 
		if ( rc < 0 ) {
			perror("write");
			exit(1);
		}
	}

	/* close sockets
	*/
	close(msgsock);
	close(sock);

	return(0);
}


/* 
 * create socket
 * bind to address
 */
initSocket()
{
	struct sockaddr_in server;
	int sock;

	/* create INTERNET,TCP socket
	*/
	sock = socket(AF_INET, SOCK_STREAM, 0); 

	if ( sock < 0 ) {
		perror("socket");
		exit(1);
	}

	server.sin_family = AF_INET;
	server.sin_addr.s_addr = INADDR_ANY;   

	server.sin_port = htons(TCPPORT);      /* specific port */

	/* bind protocol to socket
	*/
	if (bind(sock, &server, sizeof(server))) {
		perror("bind");
		exit(1);
	}
	return(sock);
}

/* read from socket. Read call issued to kernel
 * may return with incomplete data. This routine
 * must check that and force a complete read.
 */
doRead(sock, buf, amountNeeded)
int sock;
char *buf;
int amountNeeded;
{
	register int i;
	int rc;
	char *bpt;
	int count = amountNeeded;
	int amtread;

	bpt = buf;
	amtread = 0;
 
again:
	if ( (rc = read(sock,bpt,count)) < 0 ) {
		perror("doRead: reading socket stream");
		exit(1);
	}
	amtread += rc;

	if ( amtread < amountNeeded ) {
		count = count - rc;	
		bpt = bpt + rc;
		goto again;
	}
}

