출처 : http://hengki.net/37
- ////////////////////////////////////////////////////////////////////////
- //
- // 비동기 소켓 서버
- // 열혈강의 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();
- }