/*
 * udpclient.c
 *
 * OS: SunOS
 * compiler: cc
 *	To compile: cc -o udpclient -g udpclient.c
 *
 * udpclient assumes udpserver.c is running first.
 * It sends 'count' packets to the server which in turn writes
 * them back to the client; i.e., the server provides an echo service.
 *
 * To run:
 *	1. first run udpserver on some udp port that is not used.
 *	(you can make sure ports are not used with %netstat -a | grep udp)
*
 *	% udpserver 10000&
 *
 *	2. then run the client
 *
 * 	%udpclient localhost 10000 512
 *
 * syntax:
 *	% udpclient remote_host udp_port number_of_packets_to_echo
 *
 */

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

extern int errno;

#define ACK 'z'

#define BUFSIZE	1024

main(argc,argv)
char **argv;
{
	int rc;
	char *host;
	int port, count, size;
	unsigned char ack;
	int sock, length;
	struct sockaddr_in client;
	struct sockaddr_in from;
	int fromlen;
	struct msgbuf {
		long count;      /* current buffer count 0..n */
		char buf[BUFSIZE];
	} mbuf;
	int ackvar;
	int i;
	struct hostent *gethostbyname();
	struct hostent *h;
	int onintr();
	int nonacks,acks;

	/* syntax:
         * client host port count 
	 */
	if (argc != 4) {
		printf("syntax error: udpclient host port count\n");
		exit(1);
	}

	host = argv[1];
	port = atoi(argv[2]);
	count = atoi(argv[3]);
	
	/* catch signals in order to report their
	 * existance, them terminate. This is just
	 * for catching unexpected signals.
	 */
	for ( i = 0; i < 32; i++)
		signal(i,onintr);

	/* create INTERNET, udp datagram socket
	*/
	sock = socket(AF_INET, SOCK_DGRAM, 0); 

	if ( sock < 0 ) {
		perror("opening stream socket\n");
		exit(1);
	}

	/* fill in the server structure for the connect call
	 *
	 * family: INET meaning tcp/ip/ethernet.
	 * addr: string that represents INET number for remote system.
	 * port: string that represents tcp server number.
	 */ 
	h = gethostbyname(host);
	if ( h == 0 ) {
		fprintf(stderr,"cannot find host %s\n",host);
		exit(2);
	}
	/*
	bcopy(h->h_addr, &client.sin_addr, h->h_length);
	*/
	memcpy(&client.sin_addr, h->h_addr, h->h_length);

	client.sin_family = AF_INET;
	/* convert host short to network byteorder short
	*/
	client.sin_port = htons(port);

#ifdef CONNECT
	/* connect to remote server, then write or send.  If you use
	 * this call, you can just use send(2) as opposed to sendto(2)
	 */
	if (connect(sock, &client, sizeof(client)) < 0 ) {
		perror("udp client can't connect");
		exit(1);
	}
#endif

	fillBuf(mbuf.buf, BUFSIZE);

	for ( i = 0; i < count; i++) {

		printf("client msg no [%d]\n", i);

		mbuf.count = i;

		/* write msg to remote system
		 * sock
		 * buf
		 * sizeof (union msg)
		 * 0,
		 * client
		 * sizeof (client)
		 */
		if( rc = sendto(sock,&mbuf,sizeof (struct msgbuf),0,
			 &client,sizeof (client)) < 0 ) {
			/* buffers aren't available locally at the moment,
			 * try again.
			 */
			if (errno == ENOBUFS)
				continue;
			perror("sending datagram");
			exit(1);
		}

		fromlen = sizeof (struct sockaddr_in);

		/* read acknowledgement from remote system
		*/
		if (recvfrom(sock, &ackvar, sizeof(long), 0, &from, &fromlen) < 0 ) {
			printf("server error: errno %d\n",errno);
			perror("reading datagram");
			exit(1);
		}
		printFrom(&client,fromlen);

		if ( ackvar == ACK)
			acks++;
		else
			nonacks++;

	}
	printf("udpclient: count sent %d\n", count);
	printf("udpclient: acks %d nonacks %d\n",acks,nonacks);
	close(sock);
	return(0);
}

onintr(which)
{
	printf("signal %d\n",which);
	exit(1);
}

static char bufval =  'a';

/*
 * fill buffer with a character value
 *
 * Successive calls fill buffer with next character a-z...
 */
static
fillBuf(buf,size)
register char *buf;
register int size;
{
	register int i;

	for ( i = 0; i < size; i++)
		*buf++ = bufval;
	bufval++;
}

/*
 * print info in sockaddr structure
 */
printFrom(from,fromlen)
struct sockaddr_in *from;
{
	char *ns;
	char *inet_ntoa();

	printf("size of sockaddr %d size received %d\n",
		sizeof (struct sockaddr_in), fromlen);
	
	printf("family %x\n",from->sin_family);
	printf("port %d\n",from->sin_port&0xffff);
	/* convert inet number to inet network string
	*/
	ns = inet_ntoa(from->sin_addr);
	printf("addr %s\n",ns);
}
