Unity3D 다운로드

Unity3D는 C#, JavaScript, Boo 라는 언어로 개발이 가능 하다. 문득 이것중 어느것이 가장 효과적인 코딩을 지원 해줄까 의문이 들었다 그래서 조사에 나서 보았다. 아래 그림은 외국 사이트에서 조사된 Unity3D 개발자 언어 사용 분포도이다.

 
 보는 봐와 같이 오직 c#으로만 unity3d를 구현 하는 사람들이 오직 javascript를 이용해서 구현 하느 사람들보다 근소한 차이로 많았다 그리고 Boo는 생소 하기도하고 저조해서 일단 빼놓기로 하자  javascript, c#을 둘다 사용하여 개발하는 개발자들이 있었다. 
 이 정보로 봐선 둘중 하나를 쓰던지 둘다 쓰던지를 정해야 될꺼 같다.
 국내 다른 사람들의 정보를 알아보니.


『자기가 편한 걸 쓰는 것입니다만, C#이 더 가볍습니다. 한 가지 이유로 자바에서는 형을 var로 해서 쓰는 경우가 많은데, 형을 찾는 데 걸리는 시간이 들어 일반적인 자바스크립트같은 경우 C++에 비해 20배, Unity에서 쓰는 것같은 경우 수 배 량 구동시간이 차이가 납니다. C++에서 .Net개념을 도입한 것이 C#으로, C++같이 램관리를 철저하게 하지 않고 객체지향적알고리즘을 구현할 수 있습니다.  하여튼 요약하면 자바스크립트가 쉽긴하나 C#이 가볍고 에러도 적게나간다. 자기 편한거 쓰면 된다. 입니다』
 
라고 한다 그 즉슨 조금이나마 C#이 효율적인 코딩이 된다는 말이다.  
또 다른 의견을 들어보니

『c#을 강력하게 추천합니다. 저도 자바스크립트로 했다가 후회막급입니다. 여러가지 이유가 있습니다만 길어지고, 아무튼 c# 아니면 안되는 것도 많고, 고급 프로젝트 보면 전부다 c#입니다. 』 

라고 한다. 


나의 소견은 이러하다, 일단 초보자들은 Javascript로 공부를 시작 하는것이 좋을것이다 왜냐하면 접하기 쉬운언어이기 때문에 기초 강의 자료들 또한 Javascirpt가 많이 사용된다. 그러나 유니티로 본격적임 게임 개발을 한다면 c#을 사용하기를 권한다. C#은 실제 프로그래밍 환경과 훨씬 유사하며, 자바스크립트로는 어려운 관리를 효율적으로 할수 있는 장점이 있다.


Posted by 모과이IT
,

MFC에서 작업을 할 때, printf구문을 이용하여 출력문을 사용하면 화면상에서 확인을 할 수가 없는 불편한 점이 있다.그래서 MFC에서 코딩을 할때 간단하게 prinf구문을 이용하기 위해서는 콘솔창을 따로 띄워주는 방법이 있다.MFC에서 콘솔창을 띄우는 방법은 여러가지가 있지만, 아래와 같이 2 가지 방법으로 적용하면 된다.

● Method 1

App의 InitInstance에서 AllocConsole()을 호출한다. 

#ifdef _DEBUG

    if( !
AllocConsole() )
    {
        AfxMessage(_T("Failed to create the console!"), MB_ICONEXCLAMATION);
    }
#endif

해제하기 위해서는 ExitInstance에서 FreeColsole()을 호출한다.

#ifdef _DEBUG
    if( !FreeConsole() )
    {
        AfxMessage(_T("Failed to free the console!"), MB_ICONEXCLAMATION);
    }
#endif



● Method 2

stdafx.h에서 다음과 같이 입력한다.( 디버그 모드가 아닐때도 사용할 것이면  #ifdef #endif를 적용하지 않으면 된다. )

#ifdef _DEBUG
#pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console")
#endif

출력은 일반적인 콘솔 프로그램과 같이 cout이나 printf 등을 사용하면 된다.
(참고로 printf()는 stdio.h에 포함되어져있다) 

'개발지식창고 > MFC' 카테고리의 다른 글

MFC SDI 객체 간의 접근  (0) 2012.04.14
MDI 구조  (0) 2012.04.05
[MFC] CWebBrowser2 클래스 추가하기  (0) 2012.01.11
VS2005 -> VS2008 변환시 주의할점  (0) 2011.07.22
CCmdUI 클래스 : 사용자 인터페이스 갱신  (0) 2011.07.19
Posted by 모과이IT
,

리소스 풀어 헤치기
by 신영진(YoungJin Shin), codewiz at gmail.com, @codemaru, http://www.jiniya.net

디버그뷰나 프로세스 익스플로러같은 프로그램을 보면 실행 파일 하나로 구성되어 있는데 프로그램을 실행하면 드라이버를 로딩하는 것을 볼 수 있습니다. 어떻게 하는 걸까요? 답은 간단한데요. 해당 드라이버 파일을 리소스에 포함시켜 놓고 런타임에 풀어서 사용하는 방식입니다. 리소스에 추가하는 방법은 간단합니다. Visual Studio 리소스 뷰에서 오른쪽 클릭하시고 임포트 하시면 됩니다. sys 파일 같은건 안보이시죠. *.*로 필터링한다음 추가하시면 됩니다. 그러면 어떤 타입을 추가할지 물어볼텐데요. 아무렇게나 이름을 지어 주시면 됩니다. Binary 이런 식으로요. 자 그렇다면 이제 이렇게 추가된 리소스 파일을 런타임에 어떻게 조작하는지를 알아보도록 합시다.

조작하는 API는 소 심플합니다. FindResource, LoadResource, SizeOfResource, LockResource라는 API가 사용됩니다. 각각의 함수 사용 방법을 알아보도록 합시다.

  1. HRSRC FindResource(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType);  

FindResource 함수입니다. 이 함수는 우리가 분리해 내려고 하는 리소스를 찾는데 사용됩니다. hModule에는 리소스를 찾을 DLL의 모듈 핸들을 넣어주면 됩니다. NULL을 전달하면 현재 프로세스의 실행 파일 모듈에서 찾습니다. lpName에는 리소스 이름을 전달합니다. MAKEINTRESOURCE를 사용해서 리소스 ID를 문자열로 변환한 것을 대입해 주면 됩니다. 리소스 ID가 IDR_BINARY1이라면 MAKEINTRESOURCE(IDR_BINARY1)을 전달하면 되겠죠. lpType에는 리소스 타입을 넣어줍니다. 리소스 타입이 Binary라면 “Binary”를 전달하면 됩니다. 이렇게 넣어주면 리소스를 찾아서 해당 리소스의 핸들을 넘겨줍니다. 실패하면 NULL이 리턴되겠죠.

  1. DWORD SizeofResource(HMODULE hModule, HRSRC hResInfo);  

러소스의 크기를 구할 때 사용하는 함수입니다. hModule에는 모듈 핸들을 hResInfo에는 FindResource를 통해서 찾은 리소스 핸들을 넣어줍니다. 성공한 경우에는 리소스 크기를, 실패한 경우에는 0을 리턴합니다.

  1. HGLOBAL LoadResource(HMODULE hModule, HRSRC hResInfo);  

리소스를 로드하는 함수입니다. hModule에는 모듈 핸들을 hResInfo에는 FindResource로 찾은 리소스 핸들을 전달하면 되겠습니다. 해당 리소스의 포인터가 리턴됩니다.

  1. LPVOID LockResource(HGLOBAL hResData);  

마지막 함수입니다. LoadResource 한 메모리의 실제 포인터를 반환합니다. hResData에는 LoadResource에서 반환한 포인터를 넣어줍니다. 그럼 실제 포인터가 리턴됩니다.

종합해서 함수 하나로 만들어 봅시다. GetResourceInfo라는 함수입니다. module에서 type, name의 리소스를 찾아서 해당 리소스의 포인터를 data에 크기를 rsize에 있는지 없는지를 리턴 값으로 리턴해 주는 함수입니다.

  1. BOOL WINAPI GetResourceInfo(HMODULE module   
  2.                             , LPCTSTR type   
  3.                             , LPCTSTR name   
  4.                             , PVOID *data   
  5.                             , SIZE_T *rsize)   
  6. {   
  7.     DWORD size;   
  8.     HGLOBAL mem;   
  9.     PVOID ptr;   
  10.     HRSRC src;   
  11.   
  12.     src = FindResourceW(module, name, type);   
  13.     if(!src)   
  14.         return FALSE;   
  15.   
  16.     size = SizeofResource(module, src);   
  17.     if(!size)   
  18.         return FALSE;   
  19.   
  20.     mem = LoadResource(module, src);   
  21.     if(!mem)   
  22.         return FALSE;   
  23.   
  24.     ptr = LockResource(mem);   
  25.     if(!ptr)   
  26.         return FALSE;   
  27.   
  28.     __try  
  29.     {   
  30.         if(data)   
  31.             *data = ptr;   
  32.   
  33.         if(rsize)   
  34.             *rsize = size;   
  35.     }   
  36.     __except(EXCEPTION_EXECUTE_HANDLER)   
  37.     {   
  38.         return FALSE;   
  39.     }   
  40.   
  41.     return TRUE;   
  42. }  

여기까지만 설명하고 마칠까 하는데 아마 똑똑한 분들이라면 먼가 찜찜한 기분이 드실 거예요. 화장실가서 볼 일 보고 뭔가 정리하지 않고 나온 느낌이겠죠. 실컷 로딩하고 락하고 했는데 그것들을 해제하는 작업에 대해서는 하나도 설명하지 않았으니 말입니다. UnlockResource, FreeResource와 같은 함수들이 있긴 한데, 그것들은 사용하지 않아도 됩니다. 왜냐하면 여기서 사용하는 리소스 핸들을 구하고 하는 함수들은 로드된 모듈에서 포인터를 참조해 오는 일이거든요. 여러분이 획득한 모든 포인터 내지는 리소스는 hModule이 FreeLibrary 되는 시점에 같이 사라집니다. 따라서 별도로 해제할 필요가 없는 셈이죠. 참고로 그래도 나는 해제하겠다고 UnlockResource, FreeResource를 호출한들 실제 해당 함수에서 하는 일은 return 밖에는 없답니다.

Posted by 모과이IT
,

 
BOOL SHGetSpecialFolderPath(
  HWND hwndOwner,
  LPTSTR lpszPath,
  int nFolder,
  BOOL fCreate
);


 HWND hwndOwner,
 만약 다이얼로그 박스나 메세지 박스에 보여주려고 할때 넘겨 줘야 하는 Owner window 핸들
 
  LPTSTR lpszPath,
 특수 폴더의 드라이브와 경로를 받을 char 버퍼

  int nFolder,
  알고자 하는 특수 폴더의 CSIDL의 상수 값, 만약 가상 폴더값이 입력되었다면 이 함수는 실패 할 것이다. 

  BOOL fCreate 
 만약 기존의 특수 폴더가 존재하지 않는다면 폴더를 생성할 것인지 아닌지를 설정, 이 값이 0이 아닌 값이라면 폴더는 생성될것이다. 하지만 0이라면 폴더는 생성되지 않는다.

   

사용 예)

 TCHAR szSpecialPath[MAX_PATH] = {0};
    SHGetSpecialFolderPath(NULL, szSpecialPath, CSIDL_WINDOWS, FALSE);


  nFolder  상수 값

  CSIDL_FLAG_CREATE
  CSIDL_ADMINTOOLS
  CSIDL_ALTSTARTUP
  CSIDL_APPDATA
  CSIDL_BITBUCKET
  CSIDL_COMMON_ADMINTOOLS
  CSIDL_COMMON_ALTSTARTUP    
  CSIDL_COMMON_APPDATA  
  CSIDL_COMMON_DESKTOPDIRECTORY    
  CSIDL_COMMON_DOCUMENTS    
  CSIDL_COMMON_FAVORITES    
  CSIDL_COMMON_PROGRAMS    
  CSIDL_COMMON_STARTMENU    
  CSIDL_COMMON_STARTUP
  CSIDL_COMMON_TEMPLATES    
  CSIDL_CONTROLS    
  CSIDL_COOKIES  
  CSIDL_DESKTOP
  CSIDL_DESKTOPDIRECTORY  
  CSIDL_DRIVES  
  CSIDL_FAVORITES    
  CSIDL_FONTS    
  CSIDL_HISTORY     CSIDL_INTERNET  
  CSIDL_INTERNET_CACHE  
  CSIDL_LOCAL_APPDATA  
  CSIDL_MYMUSIC    
  CSIDL_MYPICTURES  
  CSIDL_NETHOOD  
  CSIDL_NETWORK
  CSIDL_PERSONAL  
  CSIDL_PRINTERS  
  CSIDL_PRINTHOOD  
  CSIDL_PROFILE  
  CSIDL_PROGRAM_FILES    
  CSIDL_PROGRAM_FILES_COMMON  
  CSIDL_PROGRAMS  
  CSIDL_RECENT     CSIDL_SENDTO  
  CSIDL_STARTMENU  
  CSIDL_STARTUP  
  CSIDL_SYSTEM  
  CSIDL_TEMPLATES  
  CSIDL_WINDOWS

Posted by 모과이IT
,

[MFC] CWebBrowser2

 1. 개요

Dialog Viewwebpage를 띄울수 있는 Controller이다. 정확한 용도나 사용법은 모르나, 후배가 면접을 위해 과제를 하는데 나도 처음 만져보고 접혔던 Class이다. 다른 것과는 달리 매우 사용법이 쉬워서 자주 사용할듯하다.
서론이 길면 안좋다. 바로 사용법이다.


2. 클래스 추가하기




일단 이렇게 클래스추가후 AxtiveX 컨트롤의 MFC 클래스를 선택한다



Microsoft Web Browser <1.0> 선택한후



Interface WebBowser2 고른다. 그냥 WebBrowser와는 무슨 차이인지는 모르겠다.
원하는 이름으로 바꿔도 용이하다.

이렇게 되면 해당 클래스의 생성은 완료다.

3. 변수 추가


위처럼
하면 해당하는 Header file cpp 파일이 생성이 된다.

해당 header file Include 변수를 선언하자.

1.#include "CWebBrowser2.h"
2.//...................
3./....................
4.CWebBrowser2  m_Page_Viewer;
5./.................

 

4. 초기화

 

해당 header file Include 변수를 선언하자.

Dialog 띄워야 했기 때문에 OnInitDialog 재정의 하여서 해당 부분을 넣었지만. View 경우는

OnCreate함수를 재정의 하면 되는 같다.

 

01.BOOL CDlg_WebViewer::OnInitDialog()
02.{
03.    CDialog::OnInitDialog();
04.    CRect rectCurrent;
05.    //View가 들어갈 Rect 수정도 용이하다.
06.    GetClientRect(rectCurrent);
07.    rectCurrent.right-=45;
08.    if (m_Page_Viewer.Create("Web Control", WS_CHILD | WS_VISIBLE, rectCurrent, this, 1010) == FALSE)
09.    {
10.        AfxMessageBox("웹브라우저컨트롤생성실패\n");
11.        return  FALSE;
12.        //정확한 Return값을 잘 모르겠다.
13.    }
14.    // TODO:  여기에추가초기화작업을추가합니다.
15.    return TRUE;  // return TRUE unless you set the focus to a control
16.    // 예외: OCX 속성페이지는FALSE를반환해야합니다.
17.}

 

 

5. Run


많이
쉽다. CString path 저장한후에 Navigate라는 함수를 실행하면

이쁘게 뜬다.

1.void CDlg_WebViewer::OnBnClickedConnect()
2.{
3.    //TODO: 여기에컨트롤알림처리기코드를추가합니다.
4.  
5.    CString sFilePath;
6.    sFilePath.Format("http://hantor.net");
7.    m_Page_Viewer.Navigate(sFilePath, NULL, NULL, NULL, NULL);
8.}




<완성 사진>

Posted by 모과이IT
,

typedef struct _OVERLAPPED {

    DWORD Internal; // [out] 에러코드 ex> STATUS_PENDING 

    DWORD InternalHigh; [out] 전송된 바이트 수

    DWORD Offset; // [in] 32bit 하위 파일 오프셋

    DWORD OffsetHigh; // [in] 32bit 상위 파일 오프셋

    HANDLE hEvent; // [in] 이벤트 핸들이나 데이터

} OVERLAPPED, *LPOVERLAPPED;


[in] 의 경우 ReadFile이나 WriteFile 호출 전에 초기화가 되어 있어야 한다. (일반적으로 0)

ex> OVERLAPPED o = {0};


#define HasOverlappedIoCompleted (pOverlapped) ( (pOverlapped)->Internal != STATUS_PENDING )

I/O 요청이 진행중인 경우 FALSE, 요청이 완료되었을 경우 TRUE를 반환한다. 


주의점

1. 디바이스 드라이버는 비동기 I/O 요청을 항상 FIFO 방식으로만 처리하지 않는다.

2. 비동기 I/O 요청을 시도하는 경우에라도 시스템이 알아서 동기 I/O로 처리하는 경우가 있다. 

(캐쉬에 있는 경우 이 요청이 디바이스 드라이버의 요청 목록에 삽입이 되지 않는다.)

3. ReadFile과 WriteFile은 I/O 요청이 동기적으로 수행되는 경우 0이 아닌 값 (TRUE) 을 반환한다.

I/O 요청이 비동기적으로 수행되는 경우나 에러가 발생하는 경우 FALSE를 반환한다.

 ERROR_IO_PENDING

 성공. I/O 요청이 디바이스 요청 목록에 삽입이 완료 되었다 라는 의미.

 ERROR_INVALID_USER_BUFFER

 ERROR_NOT_ENOUGH_MEMORY

 요청 목록이 꽉 찬 경우에 발생

 ERROR_NOT_ENOUGH_QUATA

 I/O 요청을 기다리는 동안 사용자가 전달한 버퍼를 잠그지 못한 경우

 4. 데이터 버퍼와 OVERLAPPED 구조체는 I/O 요청이 완료될 때 까지 옮겨지거나 삭제되지 않아야 한다.

( 때문에 매 I/O 요청마다 새로운 OVERLAPPED 구조체를 생성하고 초기화 해야 한다. )


함수가 반환되면 스레드 스택에 있는 b와 o가 삭제된다. 디바이스 드라이버가 함수가 반환된 후 

두 개의 변수에 접근을 하는 순간 AV이 발생할 것이다. 


VOID Foo (HANDLE hFile) { 

    OVERLAPPED o = { 0 }; 

    BYTE b[100]; 

    ReadFile (hFile, b, 100, NULL, &o); 

}


※ I/O 작업의 취소

1. BOOL CancelIo(HANDLE hFile); => 이 함수를 호출한 스레드가 삽입한 모든 I/O을 취소한다.

2. 모든 스레드의 모든 I/O를 완전히 취소하고 싶다면 장치에 대한 핸들을 닫으면 된다. 

3. BOOL CancelIoEx (HANDLE hFile, LPOVERLAPPED pOverlapped); => 하나의 I/O 요청만을 취소한다. 

(이 함수를 호출한 스레드의 I/O요청뿐 아니라 다른 스레드가 삽입한 I/O 요청도 취소할 수 있다.

pOverlapped에 NULL을 전달하면 hFile이 가리키는 장치에 대한 모든 I/O요청을 취소한다.)

 

'개발지식창고 > Serial통신' 카테고리의 다른 글

비동기 I/O 요청에 대한 완료 통지의 수신  (0) 2011.12.19
Overlapped I/O (2)  (0) 2011.12.19
Overlapped I/O (1)  (0) 2011.12.19
Serial 통신 Cycle  (0) 2011.11.30
Posted by 모과이IT
,

4가지 방법이 있다.


 방법

요약 

 디바이스 커널 오브젝트의 시그널링

 단일의 장치에 대해 다수의 I/O 요청을 수행하는 경우 적합하지 않다.  특정스레드가 I/O요청을 하고 다른 

스레드가 완료 통지를 수신할 수 있다.

 이벤트 커널 오브젝트의 시그널링

 단일의 장치에 대해 다수의 I/O 요청을 수행할 수 있다. 특정스레드가 I/O요청을 하고 다른 

스레드가 완료 통지를 수신할 수 있다.

 얼러터블 I/O 사용

 단일의 장치에 대해 다수의 I/O 요청을 수행할 수 있다. 항상 I/O 요청을 한 스레드가 완료 통지를 수신한다.

 IOCP 사용

 단일의 장치에 대해 다수의 I/O 요청을 수행할 수 있다. 특정 스레드가 I/O 요청을 하고 다른 스레드가 완료

통지를 수신할 수 있다. 


■ 디바이스 커널 오브젝트의 시그널링

말 그대로 디바이스 커널 오브젝트의 시그널링을 체크하는 방식이다.

예로, CreateFile() 의 결과값 (파일 디바이스의 커널 오브젝트) 을 가지고 WaitForSingleObject()를 이용하여

작업 완료시까지 대기하는 방식이다.

 

"단일 장치에 대해 다수의 I/O 요청을 수행하는 경우 적합하지 않다." 라는 의미는

동시에 두 개의 I/O 요청을 한 경우 완료되었을 때 어떤 요청의 완료인지에 대해 명확하게

파악을 할 수가 없기 때문에 적합하지 않다 라는 의미이다.

 

단적인 사용 예>

BOOL bReadDone = ::ReadFile(hFile, bBuffer, 100, NULL, &o);
DWORD dwError = ::GetLastError();

 

if ( !bReadDone && (dwError == ERROR_IO_PENDING))
{
    WaitForSingleObject(hFile, INFINITE); 
    bReadDone = TRUE;
}

 

if (bReadDone) { // 성공 }

 

■ 이벤트 커널 오브젝트의 시그널링

이벤트 커널 오브젝트의 경우는 OVERLAPPED 의 hEvent 필드를 이용하기 때문에, 어떠한 I/O 요청인지

판단을 할 수가 있다. 이 때 반드시 각 요청별로 서도 다른 이벤트 커널 오브젝트를 생성해야 한다.

 

단적인 사용 예>

OVERLADDPED oRead = {0};

oRead.hEvent = CreateEvent();

::ReadFile(hFile, bBuffer, 100, NULL, &oRead);

 

OVERLADDPED oWrite = {0};

oRead.hEvent = CreateEvent();

::WriteFile(hFile, bBuffer, _countof(bBuffer), NULL, &oWrite);

HANDLE h[2];

h[0] = oRead.hEvent;

h[1] = oWrite.hEvent;

 

DWORD dw = WaitForMultipleObjects(2, h, FALSE, INFINITE);

// Read가 끝났는지, Write가 끝났는지 확인 가능하다.

 

■ 얼러터블 I / O

스레드가 생성되면 시스템은 각 스레드별로 비동기 프로시저 콜 (APC) 큐라는 것을 하나씩 생성한다.

비동기 I/O 요청을 전달하는 함수(ReadFileEx, WriteFileEx)를 호출하는 경우

디바이스 드라이버에게 I//O 작업 완료 통지를 스레드의 APC큐에 삽입해 줄 것을 요청할 수 있다.


VOID WINAPI CompletionRoutine (DWORD dwError, DWORD dwNumBytes, OVERLAPPED* po)
{

    // dwError와 dwNumBytes는 po-> 를 이용해서도 구할 수 있다.
    wprintf(L"po->Internal : %d, dwError : %d\n", po->Internal, dwError); 
    wprintf(L"po->InternalHigh : %d, dwNumBytes : %d\n", po->InternalHigh, dwNumBytes);
}

 

int wmain(void)

{

    ReadFileEx (hFile, buffer, buffer count, &overladdped, CompletionRoutine);

   

    // 스레드가 얼러터블 상태가 되면, 시스템은 APC큐의 내용을 확인하여 큐에 삽입된 모든 항목에 대해

    // 컴플리션 루틴을 호출해 준다. 아래는 해당 스레드를 얼러터블 상태로 만드는 함수인 SleepEx을 이용한 예이다.

    SleepEx(10, TRUE);

}

 

※ 스레드를 얼러터블 상태로 변경할 수 있는 함수

DWORD SleepEx ( DWORD dwMilliseconds, BOOL bAlertable);

DWORD WaitForSingleObjectEx (...);

DWORD WaitForMultipleObjectsEx(...);

BOOL SignalObjectAndWait(...);

BOOL GetQueuedCompletionStatusEx(...);

DWORD MsgWaitForMultipleObjectsEx(...);

 

'개발지식창고 > Serial통신' 카테고리의 다른 글

OVERLAPPED  (0) 2011.12.19
Overlapped I/O (2)  (0) 2011.12.19
Overlapped I/O (1)  (0) 2011.12.19
Serial 통신 Cycle  (0) 2011.11.30
Posted by 모과이IT
,

■ 입출력 방식

▷ 동기 입출력

- 애플리케이션은 입출력 함수를 호출한 후 입출력 작업이 끝날 때까지 대기

- 입출력 작업이 끝나면 입출력 함수는 리턴하고 에플리케이션은 입출력 결과를 처리하거나 다른 작업을 진행

 

▷ 비동기 입출력

 

- 애플리케이션은 입출력 함수를 호출한 후 입출력 작업의 완료 여부와 무관하게 다른 작업을 진행

- 입출력 작업이 끝나면 운영체제는 작업 완료를 애플리케이션에게 알려줌. 이때 애플리케이션은 다른 작업을 중단하고 입출력 결과를 처리

 

▷ 입출력 방식에 따른 소켓 입출력 모델 분류

- 동기 입출력 + 비동기 통지    : Select모델, WSAAsyncSelect모델, WSAEventSelect모델 

- 비동기 입출력 + 비동기 통지 : Overlapped모델(I), Overlapped모델(II), Completion Port모델

 

■ Overlapped 모델

▷ Overlapped 모델 사용 절차

1. 비동기 입출력을 지원하는 소켓 생성

2. 비동기 입출력을 지원하는 소켓 함수 호출

3. 운영체제는 소켓 입출력 작업 완료를 애플리케이션에게 알려주고(=비동기 통지), 애플리케이션은 결과를 처리

 

▷ 비동기 통지 방식에 따른 Overlapped 모델 분류

 

▷ 입력 함수

 

▷ 출력 함수

 

▷ 관련 구조체

 

■ Overlapped 모델(I)

▷ Overlapped 모델(I)을 이용한 소켓 입출력 절차

1. 비동기 입출력을 지원하는 소켓을 생성한다. 이때 WSACreateEvent()함수를 호출하여 대응되는 이벤트 객체도 같이 생성한다.

2. 비동기 입출력을 지원하는 소켓 함수를 호출한다. 이때 WSAOVERLAPPED 구조체의 hEvent변수에 이벤트 객체 핸들값을 넣어서 전달한다. 비동기 입출력 작업이 곧바로 완료되지 않으면, 소켓 함수는 오류를 리턴하고, 오류 코드는 WSA_IO_PENDING으로 설정된다. 나중에 비동기 입출력 작업이 완료되면, 운영체제는 이벤트 객체를 신호 상태로 만들어 이 사실을 애플리케이션에게 알린다.

3. WSAWaitForMultipleEvents()함수를 호출하여 이벤트 객체가 신호상태가 되기를 기다린다.

4. 비동기 입출력 작업이 완료하여 WSAWaitForMultipleEvents()함수가 리턴하면, WSAGetOverlappedResult()함수를 호출하여 비동기 입출력 결과를 확인하고 데이터를 처리한다.

5. 새로운 소켓을 생성하면 1~4를, 그렇지 않으면 2~4를 반복한다.

 

▷ WSAGetOverlappedResult()함수

 

■ Overlapped 모델(II)

▷ Overlapped 모델(II) 동작 원리

 

▷ Overlapped 모델(II)를 이용한 소켓 입출력 절차

1. 비동기 입출력을 지원하는 소켓을 생성한다.

2. 비동기 입출력 함수를 호출한다. 이때 완료 루틴의 시작 주소를 함수 인자로 전달한다. 비동기 입출력 작업이 곧바로 완료되지 않으면, 소켓 함수는 오류를 리턴하고, 오류 코드는 WSA_IO_PENDING으로 설정된다.

3. 비동기 입출력 함수를 호출한 스레드를 alertable wait상태로 만든다. 앞에서 소개한 WaitForSingleObjectEx(), WaitForMultipleObjectsEx(), SleepEx(), WSAWaitForMultipleEvents()등 함수 중에서 적절한 것을 선택하여 사용하면 된다.

4. 비동기 입출력 작업이 완료되면, 운영체제는 완료 루틴을 호출한다. 완료 루틴에서는 비동기 입출력 결과를 확인하고 데이터를 처리한다.

5. 완료 루틴 호출이 모두 끝나면, 스레드는 alertable wait 상태에서 빠져나온다.

6. 새로운 소켓을 생성하면 1~5를 , 그렇지 않으면 2~5를 반복한다.

 

 


 

 

'개발지식창고 > Serial통신' 카테고리의 다른 글

OVERLAPPED  (0) 2011.12.19
비동기 I/O 요청에 대한 완료 통지의 수신  (0) 2011.12.19
Overlapped I/O (1)  (0) 2011.12.19
Serial 통신 Cycle  (0) 2011.11.30
Posted by 모과이IT
,

★Overlapped 입출력의 의미

이전의 전송이 끝나기 전에 새로운 전송을 시작할 수 있게끔 입출력을 하는 방식

Overlapped 입출력이라고 부른다. (중첩됬으므로...)

이런 것이 가능해지려면 일단 전송을 하는 함수(ex: send)가 비동기 방식을 취해야한다.

다시 말해서, 데이터 전송이 안끝나도 실행되자마자 리턴해야된다.

참고로 하나의 쓰레드 내에서 말하고 있는 것이다.

 

 

★Overlapped 입출력을 위한 기본 단계

1] Overlapped 소켓의 생성

지금까지와는 다르게 소켓 자체가 overlapped가 되게끔 만들어야한다.

socket()보다 더 기능이 많은 WSASocket()을 사용한다. (WSA_FLAG_OVERLAPPED 플래그)

 

2] 데이터의 전송

WSASend()와 WSARecv()를 사용하면 중첩된 방식의 데이터 입출력이 가능해진다.

다시 한 번 말하지만 이 함수가 Overlapped 입출력을 가능하게끔 하는 원리는

비동기 방식으로 전송을 하는 것에서부터 비롯된다. (asynchronized)

실행이 되면 바로 리턴된다.

리턴하고 바로 또 WSASend/WSARecv를 호출하면 중첩되는 것이 당연.

    int WSASend(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags,

                      lpOverlapped, lpCompletionRoutine);

     int WSARecv(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags,

                      lpOverlapped, lpCompletionRoutine);

     ※ lpBuffers는 WSABUF 구조체 배열의 포인터다. (pg491)

      ※ lpNumberOfBytes~ : Pointer to the number of bytes received by this call if the

                                       send/receive operation completes immediately.

여기서 문제가 생긴다. WSASend로 이루어진 전송에 대한 결과 정보를 어떻게 받느냐다.

첫 번째 방법은 "Event 커널 오브젝트를 이용하는 방법"이고

두 번째 방법은 "CALLBACK 함수 기반의 Completion Routines를 사용하는 방법"이다.

 

WSASend나 WSARecv의 여섯번째의 파라미터(lpOverlapped)가 첫 번째 방법에 쓰이고

일곱번째의 파라미터(lpCompletionRoutine)가 두 번째 방법에 쓰인다.

 

※※ 리눅스에서 writev, readv를 이용하여 Gather/Scatter 입출력을 쉽게 했었는데

       윈도우에서도 WSASend/WSARecv의 인자로

       WSABUF을 여러 개 이용하여 Gather/Scatter 입출력이 가능하다.

 

 

★첫번째 방법: Event 커널 오브젝트 기반의 Overlapped I/O

이 전에 말했듯이, WSASend나 WSARecv함수의 여섯 번째 인자를 사용하게 된다.

WSAOVERLAPPED라는 타입의 구조체를 만들어가지고 초기화시켜서 집어넣으면 된다.

실제로 WSAOVERLAPPED 구조체의 멤버를 보면 5개나 되는데

4개는 내부적으로 쓰여서 우리가 신경쓸 바 없고

나머지 1개(WSAEVENT hEvent)만 우리가 초기화시켜줘서 넣어야한다.

 

이렇게 해서 이제 "전송 또는 수신"과 "Event 커널 오브젝트"가 연결되었다.

이렇게 연결된 전송 또는 수신이 끝나는 시점이 그 Event 커널 오브젝트가 signaled가 되는 시점이다.

(또한 OVERLAPPED 구조체도 "전송 또는 수신"과 연결됬다고 봐도 된다. 끝나고 결과를 저장...)

※ 참고로 복습하자면, WSAEventSelect는 "소켓"과 "Event 커널 오브젝트"를 연결시키는 함수였다.

 

Overlapped I/O니까 n개의 전송 또는 수신이 n개의 Event 커널 오브젝트와 연결될 것이다.

n개의 전송 또는 수신은 동시다발적으로 이루어지고

Event 커널 오브젝트도 그에 맞춰서 모두 signaled로 변할 것이다.

 

그렇게 signaled가 된 Event 커널 오브젝트들을 뭘 어째야할까?

바로 지금까지 해왔던 듯이, WaitForMultipleEvents() 함수를 쓰는 것이다.

책에서는 그냥 똑같은 기능을 하는 WSAWaitForMultipleEvents()를 사용했다.

이제 WSAWaitForMultipleEvent()가 리턴하면

모든 overlapped 전송과 수신이 완료됬다는 것을 의미한다.

 

완료된 입출력의 성공/실패 여부, 입출력된 바이트 수 등에 대한 정보를 어떻게 뽑아낼까?

WSAGetOverlappedResult() 함수가 그 기능을 하는 함수다.

 

 BOOL WSAGetOverlappedResult(s, lpOverlapped, lpcbTransfer, fWait, lpdwFlags)

     - s: 입출력이 완료된 소켓의 핸들

     - lpOverlapped: WSASend 혹은 WSARecv 함수 호출시 전달했던

                            OVERLAPPED 구조체 변수의 포인터를 전달한다.

     - fWait: TRUE  >> 전송 또는 수신이 끝날 때까지 블로킹 

                FALSE >> 바로 리턴

     - lpcbTransfer, lpdwFlags는 파라미터를 이용한 일종의 리턴이다. 

     = 리턴: 입출력의 성공적인 완료 시 TRUE 리턴. 입출력이 현재 진행중이거나,

               에러가 발생해서 입출력을 완료하지 못한 경우를 실패로 보아 FALSE 리턴.

     ※ WSAWaitForMultipleEvent()를 이 전에 실행했을 경우,

         입출력이 현재 진행중이어서 FALSE를 리턴하는 케이스는 존재하지 않을 것이다.

         또한 fWait를 TRUE로 설정하더라도 블로킹 되는 케이스는 존재지 않음. 

 

 

★두번째 방법: Completion Routines 기반의 Overlapped I/O

WSASend와 WSARecv의 일곱 번째 전달 인자를 사용해야한다.

전달되는 것은 Completion Routine의 함수 포인터이다. 원형은 다음과 같이 선언한다.

 

void CALLBACK 이름(DWORD dwError, DWORD cbTransferred,

                            LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)

- dwError: 완료된 중첩된 입출력 상태 정보. 정상종료 시에 0. 그 이외는 오류 발생을 의미.

- cbTransferred: 전송된 바이트 수

- lpOverlapped: WSASend, WSARecv 함수 호출 시 전달하는 그 구조체 변수의 포인터

- dwFlags: 중첩된 입출력 함수 호출 시 설정했던 옵션 정보

     ※ dwError와 cdTransferred만 요긴하게 쓰이, 나머지 것들은 별로...

 

일단 CALLBACK의 이름에서 의미하는바와 같이 운영체제가 자신의 필요성에 따라 부르는 함수다.

그렇다면 이제 우리가 알아야할 것은... 이 것을 언제 부르는가?

또 이 함수를 통해서 우리는 얼마만큼의 자유를 가지고 있는가? 등이다.

 

일단 WSARecv에 일곱 번째 전달 인자를 사용하게 될 경우

Event 커널 오브젝트가 signaled가 되는 "조건"이

Event 커널 오브젝트 기반의 Overlapped I/O일 때(첫번째 방법)와는 완전히 다르게 된다.

signaled가 되는 조건은 CompletionRoutine이 가리키는 CALLBACK함수가

한 번 실행되어 종료된 이후이다.

 

그렇다면 CompletionRoutine이 가리키는 CALLBACK함수는 언제 실행될까?

바로 WSAWaitForMultipleEvents()를 실행시켰을 때부터 실행된다.

 

 

< 시나리오 >

WSARecv나 WSASend를 통하여, Event 커널 오브젝트와 CALLBACK 함수 연결

 ↓

WSAWaitForMultipleEvents() 호출 (Event 커널 오브젝트에 관한 정보만 파라미터로 넘김) 

 ↓

만약 전송/수신이 아직 안끝났으면 끝날 때까지 기다린 후

(CALLBACK함수는 전송/수신이 끝나기 전에는 실행대기 상태에 빠진다 - 커널에서 알아서)

 ↓

CompletionRoutine이 가리키는 CALLBACK 함수 실행 시작

 ↓

CompletionRoutine이 가리키는 CALLBACK 함수 종료

 ↓

WSARecv로 CALLBACK함수와 연결시켰던 Event 커널 오브젝트가 signaled가 되는 시점

 ↓

WSAWaitForMultipleEvents() 종료

 

참고로 WSAWaitForMultipleEvents 함수를 대신해서 SleepEx 함수를 사용할 경우

I/O completion callback function(CALLBACK 함수)가 불려질 때까지

블로킹 상태에 빠진다. 이럴 경우 Event 커널 오브젝트를 생성할 필요가 없어진다.

더 깔끔해진다고나할까?

 

DWORD SleepEx(

  DWORD dwMilliseconds// time-out interval
  BOOL bAlertable        // early completion option
);

 


 

/* EXAMPLE */

void CALLBACK CompletionRoutine(DWORD error, DWORD szRecvBytes,

                                                      LPWSAOVERLAPPED lpOverlapped, DWORD flags){
     if(error != 0) //에러 발생 시
          ErrorHandling("CompletionRoutine error");
     recvBytes = szRecvBytes;                //전역 recvBytes    
     buf[szRecvBytes] = 0;                     //전역 buf[]
     printf("수신한 메시지: %s \n", buf);
}

'개발지식창고 > Serial통신' 카테고리의 다른 글

OVERLAPPED  (0) 2011.12.19
비동기 I/O 요청에 대한 완료 통지의 수신  (0) 2011.12.19
Overlapped I/O (2)  (0) 2011.12.19
Serial 통신 Cycle  (0) 2011.11.30
Posted by 모과이IT
,

1. 컴포트 셋팅 초기화(Init2TTY)
2. 초기화한 포트을 연다. (OpenCommPort)
  - Read, Write 하기위한 각각의 이벤트 생성
  - File 형식으로 생성 overapped I/O옵션으로 생성
  - 컴포트에서 데이터를 교환하는 방법을 char로 설정 ( SetCommMask( idComDev, EV_RXCHAR ))
  - Buffer의 크기설정 ( SetupComm())
  - 디바이스 초기화. 혹시나 쓰레기값 청소를 위해서 ( PurgeComm))
  - Read, Write의 TimeOut 설정 (SetCommTimeouts())
 

'개발지식창고 > Serial통신' 카테고리의 다른 글

OVERLAPPED  (0) 2011.12.19
비동기 I/O 요청에 대한 완료 통지의 수신  (0) 2011.12.19
Overlapped I/O (2)  (0) 2011.12.19
Overlapped I/O (1)  (0) 2011.12.19
Posted by 모과이IT
,