//////////////////////////////////////////////////////////////////////////////////////////////////
//
// 비동기 소켓 서버
// 열혈강의 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();
}