/******************************************************************************\
* ioctl.c - TCP server
*
*       This is a part of the Microsoft Source Code Samples.
*       Copyright (C) 1996 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/



#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define DEFAULT_PORT 5001

int ReadAndEcho(SOCKET , char *,int ) ;
int WriteMessage(SOCKET , char *,int ) ;

void Usage(char *progname) {
	fprintf(stderr,"Usage\n%s -e [endpoint] -i [interface]\n",
		progname);
	fprintf(stderr,"Where:\n");
	fprintf(stderr,"\tendpoint is the port to listen on\n");
	fprintf(stderr,"\tinterface is the ipaddr (in dotted decimal notation)");
	fprintf(stderr," to bind to\n");
	fprintf(stderr,"Defaults are 5001 and INADDR_ANY\n");
	WSACleanup();
	exit(1);
}
int main(int argc, char **argv) {

	char Buffer[128];
	char *interface= NULL;
	unsigned short port=DEFAULT_PORT;
	int fromlen;
	int i, ioctl_opt =1;
	struct sockaddr_in local, from;
	WSADATA wsaData;
	SOCKET listen_socket, msgsock;
	fd_set readfds, writefds, exceptfds;

	/* Parse arguments */
	if (argc >1) {
		for(i=1;i <argc;i++) {
			if ( (argv[i][0] == '-') || (argv[i][0] == '/') ) {
				switch(tolower(argv[i][1])) {
					case 'i':
						interface = argv[++i];
						break;
					case 'e':
						port = atoi(argv[++i]);
						break;
					default:
						Usage(argv[0]);
						break;
				}
			}
			else
				Usage(argv[0]);
		}
	}
	
	if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
		fprintf(stderr,"WSAStartup failed with error %d\n",WSAGetLastError());
		WSACleanup();
		return -1;
	}
	
	if (port == 0){
		Usage(argv[0]);
	}

	//
	// The fd sets should be zeroed out before using them to prevent errors.
	FD_ZERO(&readfds);
	FD_ZERO(&writefds);
	FD_ZERO(&exceptfds);
	memset(Buffer,0,sizeof(Buffer));

	local.sin_family = AF_INET;

	//
	// bind to specific interface if desired.

	local.sin_addr.s_addr = (!interface)?INADDR_ANY:inet_addr(interface); 

	/* 
	 * Port MUST be in Network Byte Order
	 */
	local.sin_port = htons(port);

	listen_socket = socket(AF_INET, SOCK_STREAM,0); // TCP socket
	if (listen_socket == INVALID_SOCKET){
		fprintf(stderr,"socket() failed with error %d\n",WSAGetLastError());
		WSACleanup();
		return -1;
	}
	//
	// bind() associates a local address and port combination with the
	// socket just created. 

	if (bind(listen_socket,(struct sockaddr*)&local,sizeof(local) ) 
		== SOCKET_ERROR) {
		fprintf(stderr,"bind() failed with error %d\n",WSAGetLastError());
		WSACleanup();
		return -1;
	}

	//
	// start listening on the socket for incoming connections
	//
	if (listen(listen_socket,5) == SOCKET_ERROR) {
		fprintf(stderr,"listen() failed with error %d\n",WSAGetLastError());
		WSACleanup();
		return -1;
	}
	printf("%s: Listening on port %d\n",argv[0],port);

	//
	// Set the socket to non-blocking mode.
	//
	if (ioctlsocket(listen_socket,FIONBIO,&ioctl_opt) == SOCKET_ERROR) {
		fprintf(stderr,"ioctlsocket failed %d\n",WSAGetLastError());
		WSACleanup();
		return -1;
	}
	//
	// The structure of the loop below is very simple. We only accept one
	// connection at a time. As soon as another client connects, we
	// disconnect the first one, and start talking to the new client.
	// All this server does is to echo the data received on the socket
	// back to the client.
	//
	// This is not a very realistic server, but it does serve to show that
	// select() does not scale very well on win32. If we were dealing
	// with more than one client, we would have to have a list of sockets
	// that are in each fdset to be able to check them when select()
	// returns.
	//
	while(1) {

		//
		// A socket in the listen() state becomes ready to read when a
		// client connects to it. An accept() will complete without
		// blocking.
		// Since select sets the sockets that are ready to be read from or
		// written to, we have to include listen_socket in the fdset each time
		// through the loop.
		//

		FD_SET(listen_socket,&readfds);

		i = select(0,&readfds,&writefds,&exceptfds,NULL);
		if (i == SOCKET_ERROR) {
			fprintf(stderr,"select failed %d\n",WSAGetLastError());
		}
		if (i==0){
			fprintf(stderr,"Select returned no fds ready\n");
		}

		if (FD_ISSET(listen_socket, &readfds)){
			//
			// close the previous client socket. 
			// We must also clear it from the fdset to prevent select()
			// from failing.
			//
			closesocket(msgsock);
			FD_CLR(msgsock,&readfds);
			FD_CLR(msgsock,&writefds);
			fromlen = sizeof(from);
			msgsock= accept(listen_socket,(struct sockaddr*)&from,&fromlen);
			if (msgsock == INVALID_SOCKET) {
				fprintf(stderr,"accept failed %d\n",WSAGetLastError());
				WSACleanup();
				return -1;
			}
			FD_SET(msgsock,&writefds);
			FD_SET(msgsock,&readfds);
			continue;
		}
		if (FD_ISSET(msgsock,&readfds) ) {
			//
			// socket is ready to read, i.e., there is data on the socket.
			//
			if (ReadAndEcho(msgsock,Buffer,sizeof(Buffer))<0) {
				fprintf(stderr,"terminating connection\n");
				FD_CLR(msgsock,&readfds);
				FD_CLR(msgsock,&writefds);
				closesocket(msgsock);
				continue;
			}
		}
		if (FD_ISSET(msgsock,&writefds) ){
			if (WriteMessage(msgsock,Buffer,sizeof(Buffer)) <=0) {
				fprintf(stderr,"terminating connection\n");
				FD_CLR(msgsock,&readfds);
				FD_CLR(msgsock,&writefds);
				closesocket(msgsock);
				continue;
			}
		}
		FD_SET(msgsock,&writefds);
		FD_SET(msgsock,&readfds);
	}
}
int ReadAndEcho(SOCKET insock, char *Buffer,int size) {
	int rc;

	rc = recv(insock,Buffer,size,0);

	if (rc == SOCKET_ERROR) {
		fprintf(stderr,"recv() failed with error %d\n",WSAGetLastError());	
		return -1;
	}
	if (rc ==0) {
		fprintf(stderr,"Connection closed by client\n");
		return 0;
	}
	printf("Received [%s] from client\n",Buffer);
	return rc;
}
int WriteMessage(SOCKET outsock, char *Buffer,int size) {
	int rc;
	int lasterr;

	printf("Sending [%s] to client\n",Buffer);
	rc = send(outsock,Buffer,size, 0);

	if (rc == SOCKET_ERROR) {
	  lasterr = WSAGetLastError();
	  if (lasterr == WSAEWOULDBLOCK)
		return 0;
	  else {
		fprintf(stderr,"send() failed with error %d\n",lasterr);	
		return -1;
	  }
	}
	if (rc ==0) {
		fprintf(stderr,"Connection closed by client\n");
	}
	return rc;
}
