Computer(IT)/Visual Studio C++

C++ 소켓 서버 소스

약탄치킨 2008. 10. 11. 02:53
반응형

SelectServer.cpp
//===========================================================
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>

#define BUFSIZE 512

// 소켓 정보 저장을 위한 구조체
//-------------------------------------------------------------------
struct SOCKETINFO
{
 SOCKET sock;
 char buf[BUFSIZE+1];
 int recvbytes;
 int sendbytes;
};

// 플레이어정보
struct PLAYER{
 float ptx;
 float pty;
 
 float pbx;
 float pby;
 float pbz;

 float psx;
 float psy;
 float psz;

 int count;
 int nID;
}player_1;

struct PLAYER_sck{
 unsigned int sck1;
 unsigned int sck2; 
}player_sck;

int nTotalSockets = 0;

SOCKETINFO *SocketInfoArray[FD_SETSIZE];

// 소켓 관리 함수
BOOL AddSocketInfo(SOCKET sock);
void RemoveSocketInfo(int nIndex);

//----------------------------------------------------------------------
// 오류 출력 함수
/* ++++++++++++++++++++원위치
void err_quit(char *msg);
void err_display(char *msg);
*/
void err_quit(char *msg)
{
 LPVOID lpMsgBuf;
 FormatMessage(
  FORMAT_MESSAGE_ALLOCATE_BUFFER|
  FORMAT_MESSAGE_FROM_SYSTEM,
  NULL, WSAGetLastError(),
  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  (LPTSTR)&lpMsgBuf, 0, NULL);
 MessageBox(NULL, (LPCTSTR)lpMsgBuf, msg, MB_ICONERROR);
 LocalFree(lpMsgBuf);
 exit(-1);
}

// 소켓 함수 오류 출력
void err_display(char *msg)
{
 LPVOID lpMsgBuf;
 FormatMessage(
  FORMAT_MESSAGE_ALLOCATE_BUFFER|
  FORMAT_MESSAGE_FROM_SYSTEM,
  NULL, WSAGetLastError(),
  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  (LPTSTR)&lpMsgBuf, 0, NULL);
 printf("[%s] %s", msg, (LPCTSTR)lpMsgBuf);
 LocalFree(lpMsgBuf);
}

int main(int argc, char* argv[])
{
 int retval;
 
 

 // 윈속 초기화
 WSADATA wsa;
 if(WSAStartup(MAKEWORD(2,2), &wsa) != 0)
  return -1;

 // socket()
 SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);
 if(listen_sock == INVALID_SOCKET) err_quit("socket()");

 // bind()
 SOCKADDR_IN serveraddr;
 ZeroMemory(&serveraddr, sizeof(serveraddr));
 serveraddr.sin_family = AF_INET;
 serveraddr.sin_port = htons(9000);
 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
 retval = bind(listen_sock, (SOCKADDR *)&serveraddr, sizeof(serveraddr));
 if(retval == SOCKET_ERROR) err_quit("bind()");
 
 // listen()
 retval = listen(listen_sock, SOMAXCONN);
 if(retval == SOCKET_ERROR) err_quit("listen()");

 //----------------------------------------------------------------------
 // 넌블로킹 소켓으로 전환
 u_long on = TRUE;
 retval = ioctlsocket(listen_sock, FIONBIO, &on);
 if(retval == SOCKET_ERROR) err_display("ioctlsocket()");
 //----------------------------------------------------------------------

 // 데이터 통신에 사용할 변수
 //-----------------------------------------------------------------------
 FD_SET rset;
 FD_SET wset;
 //------------------------------------------------------------------
 SOCKET client_sock;
 SOCKADDR_IN clientaddr;
 int addrlen;

 while(1){
  // 소켓 셋 초기화
  FD_ZERO(&rset);
  FD_ZERO(&wset);
  FD_SET(listen_sock, &rset);
  for(int i=0; i<nTotalSockets; i++){
   if(SocketInfoArray[i]->recvbytes > SocketInfoArray[i]->sendbytes)
    FD_SET(SocketInfoArray[i]->sock, &wset);
   else
    FD_SET(SocketInfoArray[i]->sock, &rset);
  }

  // select()
  retval = select(0, &rset, &wset, NULL, NULL);
  if(retval == SOCKET_ERROR) err_quit("select()");

  // 소켓 셋 검사(1): 클라이언트 접속 수용
  if(FD_ISSET(listen_sock, &rset)){
   addrlen = sizeof(clientaddr);
   client_sock = accept(listen_sock, (SOCKADDR *)&clientaddr, &addrlen);
   if(client_sock == INVALID_SOCKET){
    if(WSAGetLastError() != WSAEWOULDBLOCK)
     err_display("accept()");
   }
   else{
    printf("[서버] 클라이언트 접속: IP 주소=%s, 포트 번호=%d\n",
     inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
    // 소켓 정보 추가
    if(AddSocketInfo(client_sock) == FALSE){
     printf("[TCP 서버] 클라이언트 접속을 해제합니다!\n");
     closesocket(client_sock);
    }
   }
  }

  // 소켓 셋 검사(2): 데이터 통신
  for(int i=0; i<nTotalSockets; i++){
   SOCKETINFO *ptr = SocketInfoArray[i];
   if(FD_ISSET(ptr->sock, &rset)){
    // 데이터 받기
    //retval = recv(ptr->sock, ptr->buf, BUFSIZE, 0);
    
    retval = recv(ptr->sock,(char*)&player_1, sizeof(PLAYER), 0);

    //Check Connect data
    //printf("Receive Data player_1.ptx : %3.3f pty : %3.3f nid : %d \n",player_1.ptx,player_1.pty,player_1.nID);

    if(retval == SOCKET_ERROR){
     if(WSAGetLastError() != WSAEWOULDBLOCK){
      err_display("recv()");
      RemoveSocketInfo(i);
     }
     continue;
    }
    else if(retval == 0){
     RemoveSocketInfo(i);
     continue;
    }
    ptr->recvbytes = retval;
    // 받은 데이터 출력
    addrlen = sizeof(clientaddr);
    getpeername(ptr->sock, (SOCKADDR *)&clientaddr, &addrlen);
    ptr->buf[retval] = '\0';
    player_1.count = nTotalSockets;
    /*printf("[TCP/%s:%d] %s\n", inet_ntoa(clientaddr.sin_addr),
     ntohs(clientaddr.sin_port), ptr->buf);*/
    printf("[client:%d] x:%3.3f, y:%3.3f, Ball-X : %3.3f,Ball-Y : %3.3f,Ball-Z : %3.3f,nID:%d  count : %d\n",ntohs(clientaddr.sin_port),
     player_1.ptx, player_1.pty, player_1.pbx, player_1.pby, player_1.pbz, player_1.nID,player_1.count);
   }
   if(FD_ISSET(ptr->sock, &wset)){
    // 데이터 보내기
    /*
    retval = send(ptr->sock, ptr->buf + ptr->sendbytes,
     ptr->recvbytes - ptr->sendbytes, 0); */
    
    //retval = send(ptr->sock,(char*)&player_1,sizeof(PLAYER),0);

    if(nTotalSockets == 2)
    {
     if(player_sck.sck1 == ptr->sock){
      player_1.count = nTotalSockets;
      player_1.nID = 1;
      retval = send(player_sck.sck2,(char*)&player_1,sizeof(PLAYER),0);
     }
     else{
      player_1.count = nTotalSockets;
      player_1.nID = 2;
      retval = send(player_sck.sck1,(char*)&player_1,sizeof(PLAYER),0);
     }
    }
    else
    {
     player_1.count = nTotalSockets;
     player_1.nID = nTotalSockets;
     retval = send(ptr->sock,(char*)&player_1,sizeof(PLAYER),0);
    }

 

    if(retval == SOCKET_ERROR){
     if(WSAGetLastError() != WSAEWOULDBLOCK){
      err_display("send()");
      RemoveSocketInfo(i);
     }
     continue;
    }
    ptr->sendbytes += retval;
    if(ptr->recvbytes == ptr->sendbytes){
     ptr->recvbytes = ptr->sendbytes = 0;
    }
   }
  }
 }

 // 윈속 종료
 WSACleanup();
 return 0;
}

// 소켓 정보 추가
BOOL AddSocketInfo(SOCKET sock)
{
 // FD_SETSIZE - 연결 대기 소켓
 if(nTotalSockets >= (FD_SETSIZE-1)){
  printf("[오류] 소켓 정보를 추가할 수 없습니다!\n");
  return FALSE;
 }

 SOCKETINFO *ptr = new SOCKETINFO;
 if(ptr == NULL){
  printf("[오류] 메모리가 부족합니다!\n");
  return FALSE;
 }

 ptr->sock = sock;
 ptr->recvbytes = 0;
 ptr->sendbytes = 0;
 SocketInfoArray[nTotalSockets++] = ptr;
 

 if(nTotalSockets == 1)player_sck.sck1 = sock;
 else player_sck.sck2 = sock;

 return TRUE;
}

// 소켓 정보 삭제
void RemoveSocketInfo(int nIndex)
{
 SOCKETINFO *ptr = SocketInfoArray[nIndex];

 // 클라이언트 정보 얻기
 SOCKADDR_IN clientaddr;
 int addrlen = sizeof(clientaddr);
 getpeername(ptr->sock, (SOCKADDR *)&clientaddr, &addrlen);
 printf("[서버] 클라이언트 종료: IP 주소=%s, 포트 번호=%d\n",
  inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

 closesocket(ptr->sock);
 delete ptr;

 for(int i=nIndex; i<nTotalSockets; i++){
  SocketInfoArray[i] = SocketInfoArray[i+1];
 }
 nTotalSockets--;
 
}

// 소켓 함수 오류 출력 후 종료
/* 원위치 +++++++++++++++++++++++++++++++++++++++
void err_quit(char *msg)
{
 LPVOID lpMsgBuf;
 FormatMessage(
  FORMAT_MESSAGE_ALLOCATE_BUFFER|
  FORMAT_MESSAGE_FROM_SYSTEM,
  NULL, WSAGetLastError(),
  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  (LPTSTR)&lpMsgBuf, 0, NULL);
 MessageBox(NULL, (LPCTSTR)lpMsgBuf, msg, MB_ICONERROR);
 LocalFree(lpMsgBuf);
 exit(-1);
}

// 소켓 함수 오류 출력
void err_display(char *msg)
{
 LPVOID lpMsgBuf;
 FormatMessage(
  FORMAT_MESSAGE_ALLOCATE_BUFFER|
  FORMAT_MESSAGE_FROM_SYSTEM,
  NULL, WSAGetLastError(),
  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  (LPTSTR)&lpMsgBuf, 0, NULL);
 printf("[%s] %s", msg, (LPCTSTR)lpMsgBuf);
 LocalFree(lpMsgBuf);
}
*/
//===========================================================