/*
 * do a non-blocking-accept
 *
 * Steve Shah (sshah@planetoid.org)
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/time.h>

#define NIPQUAD(addr) \
         ((unsigned char *)&addr)[0], \
         ((unsigned char *)&addr)[1], \
         ((unsigned char *)&addr)[2], \
         ((unsigned char *)&addr)[3]


int main (int argc, char *argv[])
{
	struct sockaddr_in incoming_connection;
	struct sockaddr_in bound_connection;
	int incoming_fd;
	int incoming_socket;
	unsigned int incoming_connection_len;
	struct protoent *tcp;
	int port;
	in_addr_t ip_addr;
	int errcode;
	int i;
	char buf[80];
	int buflen;
	
	/*
	 * Parse command line parameters 
	 *
	 */
	
	if (argc < 2) {
		printf ("Usage: non-blocking-accept <port> [<ip>]\n");
		exit(0);
	}
	
	port = atoi(argv[1]);
	if (port < 1024 || port > 65534) {
		printf ("Port number is out of range.\n");
		exit(0);
	}
	
	if (argc == 3) {
		ip_addr = inet_addr(argv[2]);
		if (ip_addr == INADDR_NONE) {
			printf ("Invalid IP address.\n");
			exit(0);
		}
	} else {
		ip_addr = inet_addr("127.0.0.1");
	}
	
	/* 
	 * Get TCP's protocol number (needed for the socket() system call)
	 *
	 */
	
	tcp = getprotobyname("tcp");
	if (tcp == NULL) {
		printf ("Can't get protocol numbers for TCP\n");
		exit(0);
	}
	
	
	/*
	 * Create a new socket 
	 *
	 */
	
	incoming_socket = socket(AF_INET, SOCK_STREAM, tcp->p_proto);
	if (incoming_socket < 0) {
		printf ("Can't create a socket\n");
		exit(0);
	}
	
	
	/* 
	 * Bind to a specific port using the new socket
	 *
	 */
	
	bound_connection.sin_family      = AF_INET;
	bound_connection.sin_addr.s_addr = ip_addr;
	bound_connection.sin_port        = htons(port);
	
	/* Note that sockaddr_in is a internet interpretation of sockaddr */
	
	errcode = bind(incoming_socket, 
		       (struct sockaddr *)&bound_connection,
		       sizeof (struct sockaddr));
	if (errcode < 0) {
		printf ("Can't bind to port %d", port);
		perror("");
		exit(0);
	}
	
	
	/*
	 * Tell us how many incoming connections we can at most queue
	 *
	 */
	
	errcode = listen(incoming_socket, 128);
	if (errcode == -1) {
		perror ("Can't listen to bound socket");
		exit(0);
	}
	
	
	/*
	 * Make socket non-blocking
	 *
	 */
	
	if (fcntl(incoming_socket, F_SETFL, O_NDELAY) < 0) {
		perror("Can't set socket to non-blocking");
		exit(0);
	}
	
	
	/*
	 * Enter the accept loop
	 *
	 */
	
	incoming_connection_len = sizeof(struct sockaddr_in);
	
	do {
		incoming_fd = accept(incoming_socket, 
				     (struct sockaddr *)&incoming_connection,
				     &incoming_connection_len);
		if (incoming_fd != -1) {
			printf ("Accepted connection from %d.%d.%d.%d:%d\n",
				NIPQUAD(incoming_connection.sin_addr.s_addr),
				ntohs(incoming_connection.sin_port));
			for (i=10;i>=1;i--) {
				buflen = snprintf (buf, 80, "%d\n", i);
				write (incoming_fd, buf, buflen);
				sleep(1);
			}
			close (incoming_fd);
		} else {
			printf ("Waiting for a connection...\n");
			sleep(1);
		}
	} while (1);
	
	return 0;
}

    

    



