//////////////////////////////////////////////////////////////////////////////////////////////////
// 
//	비동기 소켓 서버
//	열혈강의 TCP/IP 21장의 예제를 클레스화
//	일단 에코 서버이며. 167 번째 줄에서 서버에서 해줄 코드를 입력하면 될듯..
//	


#define BUFSIZE 100		// 받아올 데이터 최대 크기
#define PORT 3000		// 포트번호 할당

// 해더파일 선언
#include <winsock2.h>
#include <iostream>

using namespace std;

// ws2_32.lib 링크
#pragma comment(lib, "ws2_32.lib")

class socketServer
{
	private:
		// 변수 선언	
		WSADATA wsaData;
		SOCKET hServSock;
		SOCKADDR_IN servAddr;

		SOCKET hSockArray[WSA_MAXIMUM_WAIT_EVENTS]; //소켓 핸들배열 - 연결 요청이 들어올 때마다 생성되는 소켓의 핸들을 이 배열에 저장. (최대64)
		SOCKET hClntSock;
		SOCKADDR_IN clntAddr;

		WSAEVENT hEventArray[WSA_MAXIMUM_WAIT_EVENTS];	// 이벤트 배열 
		WSAEVENT newEvent;
		WSANETWORKEVENTS netEvents;

		int clntLen;
		int sockTotal;
		int index, i;  
		char message[BUFSIZE];
		int strLen;

		void CompressSockets(SOCKET* hSockArray, int omitIndex, int total);
		void CompressEvents(WSAEVENT* hEventArray, int omitIndex, int total);
		void ErrorHandling(char *message);

	public:
		socketServer();
		int StartServer();
};

socketServer::socketServer()
{
	sockTotal=0;
}

int socketServer::StartServer()
{

	// 윈속 초기화 (성공시 0, 실패시 에러 코드리턴)
	if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){
		ErrorHandling("WSAStartup() error!");
	}

	
	// 소켓 생성 (성공시 핸들을, 실패시 "INVALID_SOCKET" 반환)
	hServSock = socket(PF_INET, SOCK_STREAM, 0);
	
	
	// 소켓 생성 실패 처리
	if(hServSock==INVALID_SOCKET){
		ErrorHandling("socket() error");
	}


	// 소켓 통신을 위한 기본 정보 
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servAddr.sin_port = htons(PORT);

	// 주소와 Port 할당 (바인드!!)
	if(bind(hServSock, (struct sockaddr *) &servAddr, sizeof(servAddr))==SOCKET_ERROR){
		ErrorHandling("bind() error");
	}

	// 이벤트 발생을 확인 (성공시 0, 실패시 "SOCKET_ERROR" 반환)
	newEvent = WSACreateEvent();
	if(WSAEventSelect(hServSock, newEvent, FD_ACCEPT)==SOCKET_ERROR){
		ErrorHandling("WSAEventSelect() error");
	}

	// 연결 대기 요청 상태로의 진입 (신호가 들어올때까지 대기)
	if(listen(hServSock, 5)==SOCKET_ERROR){
		ErrorHandling("listen() error");
	}  

	// 서버 소켓 핸들 정보
	hSockArray[sockTotal]=hServSock;

	// 이벤트 오브젝트 핸들 정보
	hEventArray[sockTotal]=newEvent;

	// 전체 소켓수
	sockTotal++;  

	// 루프
	while(1)
	{
		// 이벤트 종류 구분하기(WSAWaitForMultipleEvents)
		index = WSAWaitForMultipleEvents(sockTotal, hEventArray, FALSE, WSA_INFINITE, FALSE);
		index = index-WSA_WAIT_EVENT_0;

		for(i=index; i<sockTotal; i++)
		{
			index = WSAWaitForMultipleEvents(1, &hEventArray[i], TRUE, 0, FALSE);			
			
			if((index==WSA_WAIT_FAILED || index==WSA_WAIT_TIMEOUT)) continue;
			else
			{
				index = i;
				WSAEnumNetworkEvents(hSockArray[index], hEventArray[index], &netEvents);
								

				// 초기 연결 요청의 경우.
				if(netEvents.lNetworkEvents & FD_ACCEPT) 
				{
					if(netEvents.iErrorCode[FD_ACCEPT_BIT] != 0)
					{
						puts("Accept Error");
						break;
					}

					clntLen = sizeof(clntAddr);
					
					// 연결을 수락 (accept | 성공시 소켓핸들 실패시 "INVALID_SOCKET" 반환)
					hClntSock = accept(hSockArray[index], (SOCKADDR*)&clntAddr, &clntLen);

					// 이벤트 커널 오브젝트 생성(WSACreateEvent)
					newEvent=WSACreateEvent();

					// 이벤트 발생 유무 확인(WSAEventSelect)
					WSAEventSelect(hClntSock, newEvent, FD_READ|FD_CLOSE);

					hEventArray[sockTotal]=newEvent;
					hSockArray[sockTotal]=hClntSock;
					sockTotal++;
				
					printf("새로 연결된 소켓의 핸들 %d \n", hClntSock);
				}

				
				// 데이터 전송해올 경우.
				if(netEvents.lNetworkEvents & FD_READ) 
				{
					if(netEvents.iErrorCode[FD_READ_BIT] != 0)
					{
						puts("Read Error");
						break;
					}

//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//
//		서버 작업은 여기서 다하겠지..
//

					// 데이터를 받음 (message에 받은 데이터를 담음)
					strLen=recv(hSockArray[index-WSA_WAIT_EVENT_0],message, sizeof(message), 0);					
					
					// 데이터 끝에 널값 입력
					message[strLen]=0;
					
					// 에코(데이터를준 클라이언트에 다시 데이터쏘기)
					send(hSockArray[index-WSA_WAIT_EVENT_0], message, strLen,0);
					
					// 예의상 화면에도 한번 보여줘야지
					cout<<message;

//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
				}


				// 연결 종료 요청의 경우.
				if(netEvents.lNetworkEvents & FD_CLOSE)
				{
					if(netEvents.iErrorCode[FD_CLOSE_BIT] != 0)
					{
						puts("Close Error");
						break;
					}
				
					WSACloseEvent(hEventArray[index]);
					
					// 소켓 종류
					closesocket(hSockArray[index]);
					printf("종료 된 소켓의 핸들 %d \n", hSockArray[index]);

					sockTotal--;
					
					// 배열 정리.
					CompressSockets(hSockArray, index, sockTotal);
					CompressEvents(hEventArray, index, sockTotal);
				}

			}//else
		}//for
	}//while

  
  
	// 할당 받은 리소스 반환.
	WSACleanup();
	
	
	return 0;
}

/************************************
/* 
/*	CompressSockets
/*
*/
void socketServer::CompressSockets(SOCKET* hSockArray, int omitIndex, int total)
{
	int i;
	
	for(i=omitIndex; i<total; i++)
	{
		hSockArray[i]=hSockArray[i+1];
	}
}

/************************************
/* 
/*	CompressEvents
/*
*/
void socketServer::CompressEvents(WSAEVENT* hEventArray, int omitIndex, int total)
{
	int i;

	for(i=omitIndex; i<total; i++)
	{
		hEventArray[i]=hEventArray[i+1];
	}
}

/************************************
/* 
/*	ErrorHandling
/*
*/
void socketServer::ErrorHandling(char *message)
{	
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}




// 한번 실행해보는 메인함수!!
/************************************
/* 
/*	main
/*
*/
int main(){

	// 서버 인스턴트
	socketServer server;
	
	// 서버시작
	server.StartServer();
}