내가만든/라인들2016. 2. 28. 16:23

C++/IOCP

메인 코드만 저장. 나머지 내용은 SVN에 있음.

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#include <windows.h>
#include <process.h>

#define MESSAGE_SIZE 100
#define MAX_CLIENT 3

using namespace std;
struct E_DEFINE
{
 enum ENUM
 {
  MAX_USER = 3,
 };
};
// 전역 변수
SOCKET stClientSocket[MAX_CLIENT]; // 접속중인 클라이언트 소켓
int g_nClientCnt = 0;    // 접속중인 클라이언트의 수
HANDLE g_hIocp = 0;     // IOCP 핸들 값

// 소켓 정보 구조체 OVERLAPPED로 캐스팅 되어서 넘어간다
typedef struct SocketInfo
 : public OVERLAPPED
{
 WSAOVERLAPPED m_OverLapped;
 SOCKET m_sSocket;
 WSABUF m_WsaDataBuf;
 int m_nReadLen;
 int m_nWriteLen; 
 char m_cBuff[MESSAGE_SIZE];
 SOCKADDR_IN m_siSocketAddr;
}SOCKETINFO, *PSOCKETINFO;

void SendMSG(char * message, int len, SOCKETINFO * pSocketInfo ); // 접속중인 유저들에게 패킷 broadcast 함수
DWORD WINAPI Thread_Func( LPVOID parm );       // IOCP 입력 결과에 의해서 사용될 함수
void ClientDisconnect( SOCKETINFO * pSocketInfo );     // 클라이언트 접속 종료 처리 함수
void ConsoleLog( char * strLOG );         // 콘솔 로그 출력 함수

void main()
{
 ConsoleLog("Server Start");

 WSADATA wsaData;
 SOCKET hServSock;
 SOCKADDR_IN servAddr, clntAddr;
 SOCKETINFO * pSocketInfo;

 char message[] = "Hello World";
 char RecvBuff[100];
 char szPort[] = "50001";

 HANDLE hThread;
 DWORD hThreadID;
 
 if( 0 != WSAStartup(MAKEWORD( 2, 2 ), &wsaData ) )
 {
  ConsoleLog( "StartUp ERROR!" );
 }

 hServSock = socket( PF_INET, SOCK_STREAM, 0 );
 if( INVALID_SOCKET == hServSock )
 {
  ConsoleLog( "Socket Open ERROR!" );
 }

 ZeroMemory( &servAddr, sizeof(servAddr) );
 servAddr.sin_family = AF_INET;
 servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
 servAddr.sin_port = htons(atoi(szPort));

 if( SOCKET_ERROR == bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)))
 {
  ConsoleLog( "Bind ERROR!" );
 }

 if( SOCKET_ERROR == listen( hServSock, 5 ))
 {
  ConsoleLog( "Listen ERROR!" );
 }

 g_hIocp = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );

 SYSTEM_INFO stSysInfo;
 GetSystemInfo( &stSysInfo );
 const int dwProcessors = stSysInfo.dwNumberOfProcessors * 2 + 2;
 for( int i = 0; i < dwProcessors; i++ )
 {
  hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)Thread_Func, 0, 0, &hThreadID );
 }

 while(TRUE)
 { 
  SOCKET hClientSock;
  SOCKADDR_IN hSockAddr;

  int nRead = MESSAGE_SIZE;
  int nFlags = 0;
  int nSockAddrLen = sizeof( hSockAddr );

  hClientSock = accept(hServSock, (SOCKADDR*)&hSockAddr, &nSockAddrLen );
  if( INVALID_SOCKET == hClientSock )
  {
   ConsoleLog( "Accept ERROR!" );
  }

  pSocketInfo = new SOCKETINFO;
  pSocketInfo->m_nReadLen  = 0;
  pSocketInfo->m_nWriteLen = 0;
  pSocketInfo->m_sSocket  = hClientSock;
  pSocketInfo->m_cBuff[0]  = '\0';
  memcpy( &(pSocketInfo->m_siSocketAddr), &hSockAddr, nSockAddrLen );
  
  ZeroMemory( &(pSocketInfo->m_OverLapped), sizeof(pSocketInfo->m_OverLapped) );
  pSocketInfo->m_WsaDataBuf.len = MESSAGE_SIZE;
  pSocketInfo->m_WsaDataBuf.buf = pSocketInfo->m_cBuff;

  g_hIocp = CreateIoCompletionPort( (HANDLE)hClientSock , g_hIocp, (unsigned long)pSocketInfo, 0 );
  if( nullptr == g_hIocp )
  {
   ConsoleLog( "CreateIoCompletion ERROR!" );
   delete pSocketInfo;
   continue;
  }

  if( SOCKET_ERROR == WSARecv( pSocketInfo->m_sSocket, &(pSocketInfo->m_WsaDataBuf), 1, (LPDWORD)(&pSocketInfo->m_nReadLen), (LPDWORD)(&nFlags), &(pSocketInfo->m_OverLapped), NULL ) );
  {
   int nRet = WSAGetLastError();
   if( WSAGetLastError() != WSA_IO_PENDING )
   {
    ConsoleLog( "WSARecv ERROR!" );
    delete pSocketInfo;
    continue;
   }
  }

  OVERLAPPED stOverLapped;
  WSABUF dataBuf;
  dataBuf.buf = message;
  dataBuf.len = strlen(message);
  ZeroMemory( &stOverLapped, sizeof(OVERLAPPED) );
  DWORD dwBufferProgress;
  if( WSASend( hClientSock, &dataBuf, 1, (LPDWORD)&dwBufferProgress, 0, NULL, NULL )  == SOCKET_ERROR )
  {
   if( WSAGetLastError() != WSA_IO_PENDING )
   {
    ConsoleLog( "WSASend ERROR" );
   }
  }  

  // 클라이언트 빈자리 검사 및 인원 제한
  int nEmptyClientSlot = 0;
  for( int nEmptyClientSlot = 0; nEmptyClientSlot<MAX_CLIENT; nEmptyClientSlot++ )
  {
   if( 0 == stClientSocket[nEmptyClientSlot] )
   {
    stClientSocket[g_nClientCnt] = hClientSock;
    char strClinetInfo[100]  = "";  
    sprintf( strClinetInfo, "Client %d accept || IP : %s", g_nClientCnt, inet_ntoa(hSockAddr.sin_addr) );
    ConsoleLog( strClinetInfo );
    g_nClientCnt++;
    break;
   }
  }  
 }

 // Socket Close
 closesocket(hServSock);
 WSACleanup();

 return;
}

// 접속중인 전체 유저에게 Send하는 함수
void SendMSG(char * message, int len, SOCKETINFO * pSocketInfo )
{
 DWORD dwBufferProgress;
 int nRetVal ;
 int nError;
 for( int i = 0; i < g_nClientCnt; i++ )
 { 
  if( 0 == stClientSocket[i] )
  {
   continue;
  }
  nRetVal = WSASend( stClientSocket[i], &pSocketInfo->m_WsaDataBuf, 1, &dwBufferProgress, 0, NULL, NULL );
  nError = WSAGetLastError();
  //send( stClientSocket[i], pSocketInfo->m_WsaDataBuf.buf, len, 0);
 }
}

DWORD WINAPI Thread_Func(LPVOID parm)
{
 HANDLE hCompletionPort = (HANDLE)parm;

 DWORD dwTransFerred = 0;
 int nFlag = 0;
 int nKey = 0;

 int nRet = 0;
 int nErrorNum = 0;

 SOCKETINFO * pSocketInfo;
 OVERLAPPED *pOverLapped;

 while(TRUE)
 {
  GetQueuedCompletionStatus( g_hIocp, (LPDWORD)&dwTransFerred, (LPDWORD)&nKey, (LPOVERLAPPED*)(&pOverLapped), INFINITE );
  pSocketInfo = (SOCKETINFO*)nKey;

  if( 0 == dwTransFerred )
  {
   ClientDisconnect( pSocketInfo );
   closesocket( pSocketInfo->m_sSocket );
   delete pSocketInfo;
   continue;
  }

  if( nullptr == pSocketInfo )
  {
   continue;
  }

  if( 0 != dwTransFerred )
  {
   SendMSG(pSocketInfo->m_cBuff, dwTransFerred, pSocketInfo );
  }

  ZeroMemory( &(pSocketInfo->m_OverLapped), sizeof( OVERLAPPED ));
  pSocketInfo->m_WsaDataBuf.len = MESSAGE_SIZE;
  pSocketInfo->m_WsaDataBuf.buf = pSocketInfo->m_cBuff;
  nFlag = 0;


  nRet = WSARecv( pSocketInfo->m_sSocket, &(pSocketInfo->m_WsaDataBuf), 1, (LPDWORD)(&dwTransFerred), (LPDWORD)(&nFlag), &(pSocketInfo->m_OverLapped), NULL ); 
  nErrorNum = WSAGetLastError();
 }
 return 0;
}

void ClientDisconnect( SOCKETINFO * pSocketInfo )
{
 if( nullptr == pSocketInfo )
 {
  return;
 }

 char strClinetInfo[100]  = "";
 for( int i = 0; i < MAX_CLIENT; i++ )
 {
  if( pSocketInfo->m_sSocket == stClientSocket[i] )
  {
   sprintf( strClinetInfo, "Client %d Disconnect || IP : %s", i, inet_ntoa(pSocketInfo->m_siSocketAddr.sin_addr) );

   stClientSocket[i] = 0;
   g_nClientCnt--;
   if( g_nClientCnt < 0 )
   {
    g_nClientCnt = 0;
   }
   break;
  }
 } 
 
 ConsoleLog( strClinetInfo );
}
void ConsoleLog( char * strLOG = nullptr )
{  
 SYSTEMTIME stSystemTime;
 GetLocalTime( &stSystemTime );
 char strTime[200] = "";

 if( nullptr == strLOG )
 {
  strLOG = "";
 }

 sprintf_s( strTime, "[%d%02d%02d] %s",
  stSystemTime.wYear,
  stSystemTime.wMonth,
  stSystemTime.wDay,
  strLOG);

 cout<< strTime << endl;
}

Posted by 비엔나햄