본문 바로가기
Programming/Tips(C++,C#)

0202 - TCP/IP Network Programming

by 곰네Zip 2009. 2. 2.


* TCP/IP에서 클라이언트가 서버에 요청하여 접속함.

*윈도우에서 네트워킹 사용하기 (수동버전)

* 게임접속중에 리셋키가 눌리면 서버는 감지를 못한다.
 가능한 판단기준.
 1. 좀비를 결정하는 루틴을 잘해야한다.
 2. 중복된세션이 생겼다. 기존세션을 끊는것이 우선적이다. 물론 기존세션에 알려주어야한다.
 3. 이러한 중복세션이 빈번하면 일시적인 블럭을 걸어준다.(해킹가능성고려)
 4. 서버는 우선 안정성이 최고이다.

//rawsocket : SOCKET_RAW임.. 근데 윈도우에서 잘 안쓰임

//----StdAfx.h에서
#include<winsock2.h> //확장된 기능을 사용한다.
#pragma comment(lib, "Ws2_32")

//----- App에서 넣어야할 코드

InitInstance()에서{
CString tmp = _T("");
 WSADATA WsaData;
 if(::WSAStartup(MAKEWORD(2,2),&WsaData)){
  tmp.Format(_T("윈속을 초기화하는데 실패했습니다. [ERROR CODE:%d]"),::WSAGetLastError());

  AfxMessageBox(tmp);
  return FALSE;
 }
}

ExitInstance()를 재정의
int CTCP_EchoServerApp::ExitInstance()
{
 // TODO: Add your specialized code here and/or call the base class
 ::WSACleanup(); 
 return CWinApp::ExitInstance();
}
이과정을 반드시 해야한다. 다만 고민할사항 하나. 윈속+DLL의경우...

a.EXE start (Winsock사용)
 InitInstance();
    DLL Load
       WSAStartup(); 
       WSACleanup();
    DLL Unload
    윈속API호출시.. 잘될까?
 ExitInstance();
 절대로 하지말짓이다. 초기화는 exe에서 맡아서 해주자.

*예제소스
//StdAfx.h//
#include<winsock2.h> //확장된 기능을 사용한다.
#pragma comment(lib, "Ws2_32")
typedef struct _CLIENT_CON_INFO{
 SOCKET   hSocket;
 SOCKADDR_IN  SockAddr;
} CLIENT_CON_INFO;
//////////////////////////////////////////////////
//extern variable
extern SOCKET  g_hSocketServer;
extern CPtrList  g_ptrListClient; //링크드리스트 클래스...
///////////////////////////////////////////////////
//thread function
UINT ThreadEchoServer(LPVOID pParam);
///////////////////////////////////////////////////
//define
#define U_LISTENPORT 9000
#define U_PACKSIZE  1500

//StdAfx.cpp//
SOCKET  g_hSocketServer;
CPtrList  g_ptrListClient; //링크드리스트 클래스...

/////////////////////////////////////////////////////////
//Listen Thread
UINT ThreadEchoServer(LPVOID pParam){
 CString tmp = _T("");
 int nSizeAddr = 0;
 CLIENT_CON_INFO* pNewClient = NULL;
 //create socket
 g_hSockServer = ::socket(PF_INET, SOCK_STREAM, 0);
 if( g_hSockServer == INVALID_SOCKET){
  tmp.Format(_T("Fail to create socket [Error code: %d]"),::WSAGetLastError());
  AfxMessageBox(tmp);
  return 0;
 }
 //bind하기
 SOCKADDR_IN ServerAddr;
 ::ZeroMemory(&ServerAddr,sizeof(SOCKADDR_IN));
 ServerAddr.sin_family   = AF_INET;
 ServerAddr.sin_addr.S_un.S_addr = ::htonl(INADDR_ANY);
 ServerAddr.sin_port    = ::htons(U_LISTENPORT);
 if(::bind(g_hSockServer, (SOCKADDR*)&ServerAddr,
  sizeof(SOCKADDR_IN)) == SOCKET_ERROR){
  tmp.Format(_T("bind error. [Error Code: %d]"),::WSAGetLastError());
  AfxMessageBox(tmp);
  g_hSockServer = NULL;
  return 0;
 }
 //enter listen status
 if(::listen(g_hSockServer,5) != 0){
  tmp.Format(_T("listen error. [Error Code:%d]"),::WSAGetLastError());
  AfxMessageBox(tmp);
  ::closesocket(g_hSockServer);
  g_hSockServer = NULL;
  return 0;
 }
 //wait for client connection (wait for enter accept status)
 while(TRUE){
  nSizeAddr = sizeof(SOCKADDR_IN);
  pNewClient = (CLIENT_CON_INFO*)malloc(sizeof(CLIENT_CON_INFO));
  pNewClient->hSocket = ::accept(g_hSockServer,
   (SOCKADDR*)&pNewClient->SockAddr,&nSizeAddr);
  if(pNewClient->hSocket != INVALID_SOCKET){
   //add socket handle and information to list
   g_ptrListClient.AddTail(pNewClient);
   //start thread to processing for client
   AfxBeginThread(ThreadProcessClient,(LPVOID)pNewClient);
  }else{ //invalid socket
   if(pNewClient != NULL){
    free(pNewClient);
    pNewClient = NULL;
    break;
   }
  }
 }
 //server socket handle shutdown
 tmp.Format(_T("Listne Thread Terminated. [Error Code: %d]"),::WSAGetLastError());
 AfxMessageBox(tmp);
 g_hSockServer = NULL;
 return 0;
}

////////////////////////////////////////////////////////
//Process Thread
UINT ThreadProcessClient(LPVOID pParam){ //pNewClient가 넘어옴
 CLIENT_CON_INFO* pNewClient = (CLIENT_CON_INFO*)pParam;
 int nLength = 0;
 char szBuffer[U_PACKSIZE];
 ::ZeroMemory(szBuffer,U_PACKSIZE);
 //receive information from client
 while((nLength = ::recv(pNewClient->hSocket, szBuffer, U_PACKSIZE,0))!=0){
  //error handling
  if(nLength < 0){
   break;
  }
  //processing information from client
  ::send(pNewClient->hSocket,szBuffer,nLength,0);
  ::ZeroMemory(szBuffer,U_PACKSIZE);
 }
 POSITION pos = g_ptrListClient.Find(pNewClient);
 if(pos != NULL){
  g_ptrListClient.RemoveAt(pos);
 }
 if(pNewClient != NULL){
  free(pNewClient);
  pNewClient = NULL;
 }
 OutputDebugString(_T("Close Client Socket"));
 return 0;
}

//Dlg.cpp
void CTCP_EchoServerDlg::OnButtonStartServer()
{
 // TODO: Add your control notification handler code here
 if(g_hSockServer != NULL){
  AfxMessageBox(_T("Server Already run!"));
  return;
 }
 GetDlgItem(IDC_Button_StartServer)->EnableWindow(FALSE);
 GetDlgItem(IDC_Button_StopServer)->EnableWindow(TRUE);
 AfxBeginThread(ThreadEchoServer,NULL);
 //set text
 SetDlgItemText(IDC_StaticStatus,_T("Activate"));
}
void CTCP_EchoServerDlg::OnButtonStopServer()
{
 // TODO: Add your control notification handler code here
 GetDlgItem(IDC_Button_StartServer)->EnableWindow(TRUE);
 GetDlgItem(IDC_Button_StopServer)->EnableWindow(FALSE);
 if(g_hSockServer != NULL){
  ::shutdown(g_hSockServer,SD_BOTH); //I/O정지
  ::closesocket(g_hSockServer); //소켓 닫음
  ::Sleep(100);
  g_hSockServer = NULL;
 }
 //close connected socket from client
 CLIENT_CON_INFO* pClient = NULL;
 POSITION pos = g_ptrListClient.GetHeadPosition();
 while(pos){
  pClient = (CLIENT_CON_INFO*)g_ptrListClient.GetNext(pos);
  if(pClient != NULL){
   ::shutdown(pClient->hSocket,SD_BOTH);
   ::closesocket(pClient->hSocket);
   //메모리해제를 여기서하지않고, 각 연결된 소켓들 스스로가 terminate되면서 메모리 해제가되어준다.
  }
 }
 //set text
 SetDlgItemText(IDC_StaticStatus,_T("No Activate"));
}

반응형

댓글