'yong's'에 해당되는 글 154건

  1. 2010.02.22 Database System
  2. 2010.02.21 헤더파일중복문제
  3. 2010.02.21 extern
  4. 2010.02.13 메시지 기본원리
  5. 2010.02.10 프로세스
  6. 2010.02.09 우아한 종료
  7. 2010.02.09 ATOM
  8. 2010.02.05 EventSelect(클)
  9. 2010.02.05 EventSelect(서버)
  10. 2010.02.05 EventSelect란?

Database의 정의

어느 한 조직의 여러 응용 시스템들이 공용할 수 있도록 통합, 저장된 운영 data의 집합


Database의 특징

실시간 접근성 (Read-time accessiblility)  : 사용자의 요구에 대한 즉각적인 응답
계속적인 변화(Continuous evolution) : 삽입, 삭제, 갱신 작업이 수시로 발생
동시 공용(Concureent sharing) : 여러 사용자가 동시에 자기가 원하는 data에 접근 가능
내용에 의한 참조(Content reference) : 물리적 주소가 아닌 data에 대한 참조

DBMS의 정의

사용자와 Database 사이에 위치하여 사용자의 요구에 따라 Database를 조작하고 제어하는 기능을 제공하는 소프트웨어

DBMS의 장점

  • data의 독립성 및 중복 최소화
  • 응용 프로그램의 개발 시간 단축
  • data의 무결성과 보안 보장
  • 표준화되고 일관된 data 관리 기능
  • data 동시 사용가능
  • data 회복 가능
DBMS의 단점

  • 시스템 자원 요구로 운영비 증대
  • 고급 프로그래밍 필요로 자료 처리의 복잡화
  • 장애 발생 대비를 위한 복잡한 Back up과 Recovery 작업 필요




DBMS의 기능

정의 기능 : Database의 논리적, 물리적 구조를 정의할 수 있는 기능
조작 기능 : 사용자가 Database 내의 data를 조작할 수 있도록 하기 위한 기능
제어 기능 : Database가 항상 정확하고 올바른 data를 유지하도록 하기 위한 기능



Data Language

Database를 정의, 조작, 제어하기 위하여 사용자와 Database 시스템 간에 사용하는 통신 수단으로 SQL이 하나의 예이다.

- data 정의어(DDL)
- data 조작어(DML)
- data 제어어(DCL)


DBA의 역할

- Database 설계와 운영 : Database 구성 요소 결정, schema 정의, 저장 구조와 접근 방법 설정, 보안 및 권한 부여 정책 결정, 백업, 회복 절차 수립 등의 작업 수행
- 행정 및 불평 해결 : 사용자의 요구를 받아 분석하고 불만을 해소
- 시스템 감시 및 서능 분석 : 시스템 이용도, 병목 현상, 이용 패턴, data 사용 추세, 각종 통계 등의 분석 작업 수행

DB Machine

- Database 시스템의 성능을 향상시키기 위해 사용하는 후위 컴퓨터
- 대용량의 data에 대한 빠른 처리를 위해 사용됨




스키마(Schema)

- Database의 논리적 정의


3단계 스키마

External 스키마 - 각 사용자의 입장에서 본 Database의 구조
                    - 사용자마다 서로 다른 Database 스키마를 가짐
                    - 개념 스키마에 대한 서브 스키마

Conceptual 스키마 - 조직 전체의 입장에서 본 Database의 구조
                        - 한 개의 스키마만 존재하며, 서로 다른 사용자가 공유
                        - data 객체(개체, 관계), 제약조건에 대한 명세를 유지

Physical 스키마 - 저장 장치의 입장에서 본 Database 구조
                     - 각 data 객체의 저장 구조를 표현함
                     - 내부 레코드의 형식
                     - 인덱스의 유무
                     - 저장 data 항목의 표현 방법


출처 - 선문비트









'프로그래밍 기초 > 오라클' 카테고리의 다른 글

Advanced Query  (0) 2010.02.25
Single-Row Functions  (0) 2010.02.24
기본쿼리2  (0) 2010.02.24
기본쿼리  (1) 2010.02.23
SQL문 정리  (0) 2010.02.22
Posted by 아몰라


#pragma는 단순히 define나 include와 같이 #으로 시작되는 전처리문의 하나입니다.

대단한 용법은 아닙니다.

다만 편의성과 기능성을 제공해 주죠.

현재 자신이 사용하고 있는 컴파일러가 이녀석을 지원해 준다면

이전글에서 헤더파일 중복문제 해결을 위한 방법보다 좋은 성과를 보여줄 겁니다.

이전글의 내용이 생각나지 않는 분을 위해 이곳에 이전글의 핵심 내용을 간단히 보여 드리죠.

 

// 헤더 파일의 제일 첫 부분
01: #ifndef HDR_H     // 헤더 파일명을 대문자한 매크로가 정의되어 있지 않으면
02: #define HDR_H    
// 헤더 파일명 매크로를 정의한다

......                // 헤더 파일 본 내용이 여기 들어갑니다

03: #endif            // ifndef HDR_H 에 매치됩니다

 

위와 같이 헤더 파일을 작성하게 되면 다음과 같은 과정이 일어나게 됩니다.

컴파일러와 역지사지해서 생각해 보겠습니다.

1. 처음으로 헤더 파일이 포함될 때, 컴파일러(좀 더 정확히 말하면 프리프로세서)가

01 라인을 만나게 되면 HDR_H 매크로가 정의되어 있지 않으므로 02 라인을 해석하게 됩니다.
2. 02 라인을 만나면 이제 HDR_H 매크로를 정의하게 됩니다.
3. 다음에 다시 같은 헤더 파일이 포함될 때는 HDR_H이 정의되어 있기 때문에

모든 헤더 파일의 내용을 스킵하게 됩니다. 즉, 포함되지 않는 효과가 생기게 됩니다.

 

이러한 방법을 include guard (포함 보호)라고 합니다.

이 방법도 무척 훌륭한 방법입니다.

다만 사용자 입장에서 실수할 여지가 많다는 겁니다.

매크로 이름의 충돌가능성등 말이죠.

물론 어지간한 프로젝트에서는 이런 일은 발생하기 어려울 겁니다 ^^a...

그리고 include guard는 한번 읽은 헤더파일도 일단 헤더파일의 내용을 다 읽어야 합니다.

시간낭비고 컴파일러 컴력 낭비(ㅡㅡ;) 이고 효율낭비이죠.

 

pragma는 이러한 면에서 확실히 뛰어납니다.

이녀석의 경우는 각 파일별로 프리프로세서가 include한 상태를 기억하고 있어서

한번만 include한다면 이후로 다시 읽어야하는 지루한 일은 안한다는 겁니다.

 

오~ pragma녀석... 무척좋군~

하시는 분도 있을겁니다.

하지만 안타깝게도 이녀석은 표준이 아닙니다.

즉, 이녀석을 사용하여 프로그램을 짠다면 다른 어떠한 환경에서는

이녀석을 지원해주지않는일을 보게 될 겁니다.

그 외에도 문제가 있지만 가장 큰 문제는 표준이 아니라는 점이죠.

 

그럼 이제 슬슬 고민이 되겠죠?

도대체 어떤놈을 써란말이야~~!!

결론은 이겁니다. 내부 포함보호와 외부 포함보호를 적절히 활용하라.

이전에 헤더파일 중복 문제해결방법은 내부 포함보호방법이었죠.

 

#ifndef HDR_H
#include "hdr1.h"
#endif

 

요런방법이 외부 포함 보호방법입니다.

이 방법을 사용하면 include guard의 문제점인 컴파일 속도측면에서의

비효율성 문제를 극복할 수 있습니다.

 

기억하세요.

손쉽고 편한 pragma 를 사용하여 이식성을 한껏 낮추는 것 보다는

내부 - 외부 포함 보호 방법을 적절히 활용하여 헤더파일 중복을 막는것이

더 올바른 자세란 것을요.


출처 - http://blog.naver.com/khercules

'프로그래밍 기초 > C' 카테고리의 다른 글

extern  (0) 2010.02.21
Posted by 아몰라
2010. 2. 21. 02:32
 

프로그램이 만들어지는 과정에는

전처리기 - 컴파일 - 링킹 - 실행파일생성 이런 단계로 이루어져있다.


전처리기
는 앞에 #이 붙은 것들을 처리하는데

특정 함수나 변수가 있는 라이브러리들을 끌어올때 사용한다.


컴파일
은 라이브러리를 참고해서 각각 특정언어로 작성된 코드들을

기계어코드인 오브젝트파일로 바꾸어준다.

extern 키워드는 선언한 변수나 함수가 현재 파일에는 없고 다른곳에 정의된

것이라는 것을 컴파일러에 알려주는 목적으로 사용된다.


마지막으로 링킹과정은 생성된 오브젝트들을 연결을 시켜서 하나의

실행파일로 만드는 과정이다.


컴파일 과정에서 에러가 나지 않았지만 링킹과정에서 에러가 발생하는 경우가 많이 있는데

그것을 컴파일때에 함수 정의나 변수가 없더라도 다른곳에서 선언되었다고 컴파일러가 묵시적으로 이해를 하는데,

링킹과정에서 그 함수, 변수를 못찾아서 에러가 발생하는 경우이다.
(extern으로 .h파일에 변수를 명시적 선언했지만 c파일에서 실질적으로 선언하지 않은경우)

 

extern 으로 선언된 변수는 컴파일러에게 요 변수가 다른곳에서 정의되어 있다는 것을 알려준다.

컴파일 타이밍에 메모리에 잡히지 않고  단지 컴파일러에게 이러한 변수가 있으니 현재 파일을 컴파일 할때 에러를 보내지 말라고 알려주는역할을 한다.

'프로그래밍 기초 > C' 카테고리의 다른 글

헤더파일중복문제  (0) 2010.02.21
Posted by 아몰라

 

RESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);


첫번째 인수(HWND hWnd) : 메시지를 받을 윈도우 핸들. 클래스로부터 여러 개의 윈도우가 만들어

졌을 때 어떤 윈도우로 전달된 메시지 인지를 구분해야 하므로 이 인수가 필요함.

두번째 인수(UINT iMessage) : Message ID이며 0x00 ~ 0x3FF 사이의 값이다. “WM_”으로 시작하는 상수로 정의 되어 있다.(전달된 메시지의 값)

WPARAM wParam, LPARAM lParam : 32비트 정수값이며 메시지의 추가 정보를 가진다.


메시지 큐


 

 

메시지는 시스템이나 사용자에 의해 발생한다.

키보드나 마우스 등의 사용자 입력 시 시스템은 이 입력에 대해 메시지를 발생시키며 또한

시스템 상황 변화를 통지하기 위해서도 메시지를 발생시킨다. 응용 프로그램도 윈도우간의

통신을 위해 메시지를 발생하며 특정 함수 호출에 의해 간접적으로 메시지가 발생하기도 한다.

윈도우즈라는 운영체제는 끊임없는 메시지의 생성과 처리를 무수히 반복하면서 실행되는

것 이므로 메시지는 윈도우즈 운영체제에서 중요하다고 할 수 있다.

메시지는 크게 메시지큐로 들어가는 (Queued)메시지와 큐에 들어가지 않고 곧바로

윈도우 프로시저로 보내지는 비큐(Non Queued)메시지로 구분된다.



 

윈도우 프로시져(Window Procedure) : 메시지를 처리하는 함수

 

- 윈도우 클래스당 하나씩 배정되며 메시지에 대응하는 방식을 정의하여 윈도우의 행동 양식을

   결정한다.

- 윈도우 클래스로부터 생성된 모든 윈도우의 메시지는 이 함수가 처리한다.

- 이름은 사용자가 마음대로 바꿀 수 있으나 일반적으로 WndProc이라는 이름을 사용한다.

- 4개의 인수를 가지며 정수값 하나를 리턴한다. 대부분 잘 처리했으면 0을 리턴하지만 어떤

   메시지는 처리 결과를 운영체제로 리턴하여 보고하는 경우도 있다.

Ex) WM_CREATE 는 초기화 과정에서 에러가 없으면 0을 리턴 에러가 발생했으면 -1을 리턴한다.

      WM_NCHITTEST  는 마우스가 윈도우의 어떤 부분에 있는지를 리턴한다.

  *WM_NCHITTEST : 윈도우에게 커서가 움직이거나 마우스버튼이 눌리거나 띄어졌을 때 메시지를 보낸다.



 




v  Window Procedure

모든 윈도우는 메시지를 처리하기 위해 메시지 처리 함수가 있어야 한다. 메시지 처리 함수의 모양은 아래 같다.

LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

윈도우 클래스를 만들때 메시지 처리함수의 주소를 등록한다.

v   Non-Queued Message

사용자가 윈도우를 만들기 위해 CreateWindowEx()함수를 호출하면 윈도우가 만들어 지고 윈도우가 만들어 졌다는 사실을 사용자에게 알리기 위해 WM_CREATE라는 메시지가 메시지 처리함수에 전달된다.(메시지 처리 함수가 호출된다.)

이때 WM_CREATE 메시지는 Message Queue 놓이지 않고 직접 Window Procedure 전달 되는 이를 non-Queued Message라고 한다.

입력 메시지(마우스 메세지, 키보드 메시지) 제외한 대부분의 메시지가 non-Queued Message 이다.

 

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {

switch( msg )     {

case WM_CREATE:

                           MessageBox( 0, "WM_CREATE도착", "", MB_OK);

                           return 0;

  

}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE , LPSTR lpCmdLine, int nShowCmd)

{

HWND hwnd = CreateWindowEx(…);

ShowWindow(hwnd, nShowCmd);






v  Queue Message

마우스나 키보드 메세지들은 직접 Window Procedure 전달되지 않고 메시지 Queue 놓이게 되는데 이를 Queue Message라고 한다. Queue 놓인 메시지를 처리하려면 반드시 사용자가 Queue 놓여 있는 메시지를 가져오는 메시지 루프가 필요하다.

MSG msg;

while ( GetMessage( &msg, 0, 0, 0 ) )  {

       TranslateMessage(&msg);

       DispatchMessage(&msg);

}

GetMessage() 메시지 Q WM_QUIT 메시지가 있을 경우에만 FALSE 리턴한다. 그래서 코드는 WM_QUIT메세지가 Q 들어 올때 까지는 무한 루프가 된다.

v   프로그램의 종료

사용자가 만든 윈도우가 파괴될 나오는 메시지는 WM_DESTROY 이다. 그러므로 사용자가 윈도우를 파괴 해도 프로그램은 계속 메시지 루프를 돌게 된다.( 하지만 윈도우는 파괴 되었다.- 모니터 화면에는 이상 아무 것도 없다.)

윈도우를 1 만든 경우 프로그램의 main 윈도우가 파괴 프로그램을 같이 종료 되게 하려면 WM_DESTROY 메시지에서 WM_QUIT메세지를 메시지 Q 넣는 작업을 해야 한다.

case WM_DESTROY:

       PostQuitMessage( 0 );             return 0;

v   처리하지 않은 메시지

메시지 처리 함수에서 발생된 메시지를 처리하지 않은 경우 반드시 아래 함수로 보내서 default 처리가 되게 해야 한다.

LRESULT CALLBACK DefWindowProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);









메시지 루프

 

- 사용자에 의해 입력된 메시지는 시스템 메시지 큐에 일단 저장되고 스레드 메시지 큐로 이동했다가 메시지 루프에 의해 해당 윈도우의 윈도우 프로시저로 보내져 처리된다.

 - 메시지는 최종 처리되기 직전까지 계속 큐에 유지되는데 이때 메시지는 다음과 같이 정의된 구조체의 형태로 존재한다.


Typedef struct tagMSG{HWND hwnd; UINT message; PARAM wParam; LPARAM lParam; WORD time; POINT pt;}MSG;


HWND
hWnd : 메시지를 받을 윈도우 핸들.

UINT iMessage : 전달된 메시지의 값

WPARAM wParam, LPARAM lParam : 메시지의 추가 정보를 가진다.

 

DWORD time : 메시지가 발생한 시간(time)

POINT pt : 발생시의 마우스 좌표(pt)

시간과 마우스 좌표는 모든 메시지들이 필요로 하는 정보가 아니므로 WndProc까지 전달되지 않는다.
필요할 경우 다음 두 함수를 사용하여 직접 조사해야 한다.

      ※ DWORD GetMessagePos(VOID) : 하위워드(LOWORD)x좌표, 상위워드(HIWORD)y좌표를 리턴한다.

      ※ LONG GetMessageTime(VOID) : 메시지 발생 시간을 부팅된 후 경과된 1/1000 단위로 리턴한다.


 

 - 메시지 루프는 메시지 큐에서 메시지를 꺼내 메시지 처리 함수로 보내는 일을 한다.

 - 보통 WinMain의 제일 끝부분에 위치하며 다음과 같이 그 형태가 정형화 되어 있다.


while(GetMessage(&Message,NULL,0,0)

{

     TranslateMessage(&Message);

     DispatchMessage(&Message);

}


- while문에 싸여져 있는데 이 루프는 프로그램이 끝날 때까지 계속반복된다.

    (메시지큐에 WM_QUIT[kwɪt]이라는 윈도우 메시지가 도착해 있으면 위의 반복문 조건속에 있는

     GetMessage함수는 FALSE값을 리턴하고 종료한다.)

      * WM_QUIT : PostQuitMessage 함수가 실행되면 발생하는 메시지

 

BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);

 

 - 스레드 메시지 큐에 대기중인 메시지를 꺼내 첫 번째 인수로 전달된 MSG 구조체에 복사한다.

 - 복사한 메시지를 큐에서 제거하고 TRUE리턴하며, 만약 큐에서 가져온 메시지가 WM_QUIT이면

    FALSE 리턴하여 메시지 루프를 탈출할 수 있다.

 

LPMSG lpMsg : MSG 구조체의 번지를 지정한다.

HWND hWnd : 현재 메시지를 큐에 올린 윈도우의 핸들값. 메시지를 처리해서 되돌려 줄때 필요하다.

UINT wMsgFilterMin : 회수된 메시지의 범위 중 최소값을 정수형태로 지정

UINT wMsgFilterMax : 회수된 메시지의 범위 중 최대값을 정수형태로 지정

 

*wMsgFilterMin : 첫번째 키보드 메시지인 WM_KEYFIRST 메시지 혹은 첫번째 마우스 메시지를 저장하는 WM_MOUSEFIRST 메시지 정수값을 저장한다.

*wMsgFilterMax : 마지막 키보드 메시지인 WM_KEYLAST 메시지 혹은 마지막 마우스 메시지를 저장하는 WM_MOUSELAST 메시지 정수값을 저장한다.

*wMsgFilterMin, wMsgFilterMax에 메시지의 범위를 지정하면 이 범위 내의 메시지만 가져온다.

*wMsgFilterMinwMsgFilterMax 인자의 값이 둘 다 0이면  GetMessage함수는 메시지 사용범위의  지정없이  모든 메시지를 돌려준다.

 

TranslateMessage


 
GetMessage 함수에 의해 큐로부터 가져온 메시지는 WM_QUIT가 아닌 한은 TranslateMessage 함수로 전달된다. 이 함수는 가상키 입력을 문자 입력(WM_CHAR)으로 바꾸는 역할을 하며 가상키 입력이 아닌 경우는 아무 처리도 하지 않는다.

* 컴퓨터의 가장 기본적인 입출력 장치는 아직까지는 키보드이다. 입력이 발생했을 경우 윈도우즈는 포커스를 가진 프로그램에게 키보드 메시지(WM_CHAR)를 보내주며 프로그램은 이 메시지를 받아 키보드 입력을 처리한다.(포커스(FOCUS)를 가진 프로그램이란 활성화되어 있는 윈도우를 말하며 한번에 오직 하나의 프로그램만 활성화 된다.멀티 태스킹 환경이라도 활성화될 수 있는 프로그램은 오직 하나밖에 없으며 활성화된 프로그램만 포커스를 가지고 키보드 입력을 받아들일 수 있다.


 

 DispatchMessage


메시지를
윈도우 프로시저로 보내 처리하도록 하는데 MSG 구조체의 hwnd 멤버를 보고 정확하게 목적 윈도우의 메시지 처리 함수로 배달한다. 윈도우 핸들로 부터 어떤 윈도우 프로시저에게 메시지를 전달할 것인가를 결정하는 중요한 일을 이 함수가 담당한다.




출처 - 선문비트 최강사님 도큐먼트

'API & MFC > API & 시스템프로그래밍' 카테고리의 다른 글

프로세스  (0) 2010.02.10
ATOM  (0) 2010.02.09
간단한 DLL 예제  (1) 2010.02.03
리스트뷰  (0) 2010.02.03
WM_USER And WM_APP  (0) 2010.02.02
Posted by 아몰라
정의  - 실행중인 프로그램 (파일이 실행되어 메모리에 적재되면 프로세스가 된다.)

내용 -  프로세스는 실행중인 프로그램이지만 작업의 주체는 아니다. 작업은 프로세스 내의 쓰레드가 담당한다.

프로세스는 각각 4GB의 주소 공간과 파일, 메모리, 스레드 등의 객체들을 소유하며 프로세스가 종료될 때 프로세스가 소유한 모든 자원은 운영체제의 의해 파괴된다. 모든 것은 프로세스의 의해 소유되며 스레드는 윈도우와 메시지 큐, 스택만 소유한다. 객체간의 소유 관계는 프로세스 > 스레드 > 윈도우 로 정리할 수  있다.



<프로세스 생성 함수> - WinExec, CreateProcess.

 

UINT WinExec (LPCSTR lpCmdLine, UINT uCmdShow)

한 프로그램에서 다른 프로그램을 실행하고자 할 때는 Win32 AP가 제공하는 프로세스 생성 함수를 사용한다. 프로세스를 생성하는 가장 간단한 함수는 WinExec이다

lpCmdLine

실행 시키고자 하는 프로그램의 이름, (완전 경로를 줄 수 있다.)

uCmdShow

실행 직 후 프로그램이 어떻게 보일지 지정.(기본 SW_SHOWNORMAL)

Ex)

UINT WinExec(“Notepad.exe”,SW_SHOWNORAML);

 

BOOL CreateProcess (LPCTSTR lpApplicationName, LPTSTR lpCmdLine,

LPSECURITY_ATTRIBUTES lpProcessAttributes,

LPSECURITY_ATTRIBUTES lpThreadAttributes,

BOOL bInheritHandles,  DWROD dwCreationFlags,

LPVOID lpEnvironment,  LPCTSTR lpCurrentDirectory,

LPSTARTUPINFO lpStartupInfo,

LPPROCESS_INFORMATION lpProcessInformation  );

프로세스를 생성하는 함수. 새로 만든 프로세스의 메인 윈도우가 어떻게 초기화될지 정하는 구조체인STARTUPINFO 구조체와 생성된 프로세스의 정보를 대입 받기 위한 구조체인 PROCESS_INFORMATION 구조체가 꼭 필요하다.[출처] [API] 프로세스|작성자 흡연토끼

à10개의 인수를 가지고 있는데 이 중 반드시 필요한 인수는 4가지 뿐이며 나머지는 NULL

줄 수 있다.

 

lpApplicationName

실행하고자 하는 프로세스 이름

lpCmdLine

명령행 인수를 지정한다.

bInheritHandles

상속 가능한 핸들에 대해 자식 프로세스에게 상속할지를 결정.(null)

lpStartupInfo

메인 윈도우의 초기화를 정하는 구조체.

lpProcessInformation

생성된 프로세스의 정보를 받기위한 구조체.

EX)

CreateProcess(NULL,”NotePad.exe”,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);






프로세스가 자기 자신을 종료하는 가장 간단하고 일반 적인 방법은 WM_CLOSE메시지를 보내거나 DestoryWindow로 메인윈도우를 파괴하는것이다. 이때 WM_DESTORY에서 PostQuitMessage를 호출하여 메시지 루프를 종료한다.

 

 

<프로세스 종료 함수>

 

- void ExitProcess(UNIT uExitCode);

 함수가 호출되면 프로세스는 정리 작업이 들어가고 즉각 종료된다.

 

     프로세스와 연결된 모든 DLL을 종료시키기 위해 각 DLL DllMain함수가 호출되며

        dll들은 스스로 정리 작업을 한다.

     열려진 모든 핸들을 닫는다.

     실행중인 모든 스레드를 종료한다.

     프로세스 커널 객체와 스레드 객체는 신호상태가 되며 이 객체를 기다리는 다른 프로세스는 대기상태를 해제한다.

     프로세스의 종료코드는 STILL_ACTIVE에서 ExitProcess가 지정한 종료값이 된다.

 

-BOOL TerminateProcess(HANDLE hProcess,UINT uExitCode);

 프로세스 외부에서 다른 프로세스를 종료하는 메소드, ExitProcess에 비해 종료 대상이 되는 프로세스의 핸들을 가지므로 자기 자신이 아닌 다른 프로세스를 강제로 종료 시킬 수 있다.

 

à TerminateProcess ExitProcess함수보다 더 위험하다. 이는 함수가 호출될 때는 동일한 작업이 수행되나 TerminateProcess는 연결된 DLL에게 종료 사실을 통지하지 않는다. 때문에 TerminateProcess함수로 강제로 프로세스를 종료한다면 정보를 잃어버리게 될 수 있다.


<프로세스 핸들>

윈도우즈는 GDI, USER, KERNEL의 세 가지 주요 DLL로 구성되어 있다. 각 모듈은 여러 종류의 객체를 관리하며 관리의 편의를 위해 핸들을 사용한다. 객체는 시스템 리소스를 나타내는 일종의 데이터 구조체이며 이 구조체들은 보통 덩치가 크기 때문에 좀 더 간단한 32비트 정수값의 핸들로 관리된다. 각 모듈별 객체에는 어떤 것들이 있고 특징은 어떤지 정리해보자.

모듈

객체

특징

USER

윈도우, 커서, 캐럿, 아이콘

한 오브젝트에 하나의 핸들. 시스템 전역적

GDI

, 브러시, 비트맵, 팔레트

한 오브젝트에 하나의 핸들. 프로세스 지역적

KERNEL

파일, 프로세스, 스레드, 이벤트

보안 적용. 프로세스 한정적


USER 객체 - ProA에서 윈도우를 만들고 핸들을 받으면 그 핸들을 ProB에게 전달하면 ProB는 윈도우 핸들을 사용하여 ProA가 만든 윈도우를 마음대로 만질수 있다.

GDI객체 - ProA에서 펜을 만들고 hPen 핸들을 발급받아서 ProB로 보냈다고해서 ProB가 이 펜을 사용할 수 없다. GDI 객체는 언제든지 원하는대로 생성할 수 있는데다 공유할 필요가 없기 때문에 프로세스 지역적이다.

커널객체 - 윈도우즈는 멀티 유저를 지원하는데 파일이나 프로그램 모두 소유자를 지정할 수 있고 소유자만 액세스 하도록 권한을 설정할 수 있다. 그래서 커널 객체를 만드는 CreateProcess, CreateFile, CreateMutex 등의 함수들은 모두 보안 속성을 인수로 가진다.

이런 보안상의 이유로 커널 객체는 프로세스 한정적이다.
(프로세스 한정적 - 한 프로세스가 만든 핸들은 다른 프로세스에서 곧바로 사용할 수 없되 대신 다른 프로세스가 같은 객체를 다시 오픈 하여 또 다른 핸들을 사용할 수 있다는 뜻이다.)

ProA가 ProC를 생성해서 ProC의 핸들을 받는다. 그리고 ProB에게 ProC의 핸들을 보내준다. 하지만 ProB는 ProC를 열 수 있는 권한이 아직 있는지 없는지 확인이 안되었기 때문에 사용못한다. ProB가 ProC를 액세스하고 싶다면 다음 함수로 프로세스의 ID로부터 프로세스 핸들을 다시 열어야 한다. 

ID
- 프로세스간의 구분을 위한 중복되지 않는 식별값일 뿐이며 ID로부터 직접 프로세스를 제어할 수는 없다. ID로부터 핸들을 발급받아야만 비로소 이 객체를 제어할 수 있다.

<프로세스의 핸들 얻는 함수>

 

HANDLE OpenProcess(DWORD dwDesiredAccess, BOOL blnheritHandle, DWORD dwProcessld)

dwDesiredAccess

Flag 접근

blnheritHandle

Flag 상속

dwProcessld

프로세스 identifier

 

<자기자신의 프로세스 핸들 얻는 함수>

HANDLE GetCurrentProcess (VOID);  : 필요한 인자는 없으며 자기자신의 프로세스 핸들 값을 반환한다.




<
프로세스 종료 상태를 구하는 함수>
                  
BOOL GetExitCodeProcess (HANDLE hProcess, LPWORD lpExitCode);

프로세스의 종료 상태를 구하는 인수로 프로세스의 핸들을 주면 이 프로세스의 종료 상태를 lpExitCode에 리턴 해준다.

hProcess

프로세스의 핸들

lpExitCode

DWORD의 포인터형. 프로세스의 상태코드가 넘어온다.        성공 1, 실패 0반환.

 

<사용 예>

static DWORD ProcID = 0;

static HANDLE hProc = 0;

 

switch (iMessage) {

           case WM_USER:

                     ProcID = wParam;

                     hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcID);

                     return 0;

           case WM_LBUTTONDOWN:

                     GetExitCodeProcess(hProc, &ExitCode);

           }



.[출처] [API] 프로세스|작성자 흡연토끼

'API & MFC > API & 시스템프로그래밍' 카테고리의 다른 글

메시지 기본원리  (0) 2010.02.13
ATOM  (0) 2010.02.09
간단한 DLL 예제  (1) 2010.02.03
리스트뷰  (0) 2010.02.03
WM_USER And WM_APP  (0) 2010.02.02
Posted by 아몰라

1. 소켓 연결 종료의 문제점(이는 TCP기반을 말한다.)

TCP 기반에서 closesocket()함수를 호출하게 되면 연결을 완전 종료한다는 의미이다. 여기에 한 가지 문제점이 있다.

 

만약 Host A Host B에게 메시지를 보내고 Host A closesocket 함수를 호출해 연결을 종료 했다면, Host B에서 Host A로 전송되고 있는 메시지가 있을 때, 그 메시지는 전송을 완료하지 못하고 중간에 소멸되고 말것이다. (여기서 closesocket함수는 출력 스트림과 입력 스트림을 모두 종료하는 기능을 한다.)

이 문제를 해결하기 위해 소켓 스트림의 일부만 종료(half close)하는 방법에 대해 알아볼 것이다.

 

다음은 half close를 하는 함수이다.

 

int shutdown(SOCKET s, int how);

s : half-close를 할 소켓의 핸들이다.

How : 종료모드를 인자로 전달한다.

 

다음에 how에 들어갈 종료모드들이다.

상수 값

모드

정의

0

SD_RECEIVE

입력 스트림 종료

1

SD_SEND

출력 스트림 종료

2

SD_BOTH

/ 출력 스트림 종료

 

다음과 같이 만약 출력 스트림만 종료를 하게 된다면, Host B에서 Host A로 전송중인 메시지는 입력 스트림이 아직 열려 있기 때문에 무사지 전송이 가능하게된다.

 

 

 

 

 

 

2. 출력 스트림의 종료의 필요성

메시지가 모두 무사히 전송되고 나서 closesocket를 이용해 종료하면 되지 출력 스트림의 종료가 왜 필요한 것인가?에 대해 알아보자.

 

출력 스트림을 종료하게 되면, 데이터 전송의 끝을 알려주는 EOF 메시지를 연결 되어 있는 호스트로 보내게 된다. 그리고 EOF 전송 시 상대 호스트의 데이터 수신함수(read, recv) 0을 반환한다.

 

출력 스트림을 종료하는 방법에는 shutdown 함수를 이용하여 종료 할 수도 있고, closesocket를 이용해도 입력 스트림과 함께 출력 스트림 또한 종료 되므로 EOF메시지가 전송된다.

 

다음 그림을 보면, 파일을 모두 보내고 EOF 메시지를 전송하고 있다. Clinet에서는 EOF메시지를 받고 파일이 모두 전송되었다는 것을 알아차리고 Thank you메시지를 보내게 된다. 만약 입력 스트림과 출력 스트림 모두 종료하게 된다면 Thank you 메시지는 받지 못할 것이다. 또한 EOF메시지를 보내지 않는다고 한다면, Client는 파일 전송이 언제 끝나는지도 모르는 채 파일이 전송을 계속 기다리게 되고, 한편 Server측에서는 파일을 다 전송하고 확인 메시지를 계속 기다리게 되어 server / client 모두 무한 대기 상태에 빠지게 된다.
 

 

'API & MFC > 네트워크프로그래밍' 카테고리의 다른 글

EventSelect(클)  (0) 2010.02.05
EventSelect(서버)  (0) 2010.02.05
EventSelect란?  (0) 2010.02.05
ASyncSelect 예제  (1) 2010.02.04
소켓통신의 라이브러리 사용 선택 시 고려사항  (0) 2010.02.02
Posted by 아몰라
아톰에 대해서 알기전에 아톰테이블에 대해서 먼저 알아보자.

아톰 테이블이란?

- 시스템이 유지하는 문자열 테이블
- 아톰들을 저장하는 해쉬테이블

아톰테이블에 문자열을 보관하면 저장된 문자열을 대표하는 정수값을 돌려준다. 그 값이 아톰이다.


 아톰 테이블의 종류


로컬 아톰테이블

- 지역아톰테이블에 저장
- 테이블을 만든 응용 프로그램 내에세만 사용가능
- 함수명 AddAtom, GetAtomName 등등


글로벌 아톰테이블

- 전역아톰테이블에 저장
- 시스템의 모든 응용 프로그램이 같이 사용
- 함수명 GlobalAddAtom, GloabalGetAtomName 등등

아톰함수정리

ATOM GlobalAddAtom(LPCTSTR lpString)
 <문자열 추가>
첫 번째 인자: 추가 하고자 하는 문자열
반환 값 : 아톰테이블에 추가된 문자열의 아톰을 준다.


ATOM GlobalDeleteAtom(ATOM nAtom) <문자열삭제>
첫 번째 인자: 삭제 하고자 하는 아톰
반환 값:  성공시 0 , 실패시 nAtom



UINT GlobalGetAtomName(ATOM nAtom, LPTSTR lpBuffer, int nSize)
 <아톰값으로부터 문자열 추출>
첫번재 인자: 헤당 아톰
두번째 인자: 아톰에서 찾은 문자열을 담을 버퍼공간

세 번째 인자:
버퍼공간 사이즈
반환 값:  실패 시 0


ATOM GlobalFindAtom(LPCTSTR lpString)
<문자열로부터 아톰값을 구한다.>
 

첫 번째 인자: 해당 문자열
반환 값 : 실패 시 0



아톰의 사용용도

- 문자열을 '키'로 가지는 자료들을 관리 할 때
     중복문자열을 저장하지 않는다.(Add함수로 같은 문자열을 저장하면 참조카운트가 늘어난다 1,2,3....Delete 함수로 참조인수가 0이 될 때 시스템은 아톰테이블에서 문자열삭제한다.)


- 문자열을 통한 해쉬함수가 필요한 경우








'API & MFC > API & 시스템프로그래밍' 카테고리의 다른 글

메시지 기본원리  (0) 2010.02.13
프로세스  (0) 2010.02.10
간단한 DLL 예제  (1) 2010.02.03
리스트뷰  (0) 2010.02.03
WM_USER And WM_APP  (0) 2010.02.02
Posted by 아몰라

#pragma comment(lib,"ws2_32")
#include <winsock2.h>
#include <iostream>

#define YOURIP "192.168.34.103"
#define YOURPORT 1100


struct App
{
 SOCKET sarr[WSA_MAXIMUM_WAIT_EVENTS];
 HANDLE hEarr[WSA_MAXIMUM_WAIT_EVENTS];
 HANDLE hFileArr[WSA_MAXIMUM_WAIT_EVENTS];
 int now;
};

void connectProc(int index);
void writeProc(int index);
void CloseProc(int index);
void DeleteSocket(int index);
void InitEvent(SOCKET sock);
void SendData(int index);


extern App app;




#include "event.h"

using namespace std;

App app; 

 

void main()
{
 WSADATA wsadata;
 WSAStartup(MAKEWORD(2,2),&wsadata);
 
 SOCKET sock;
 sock = socket(AF_INET, SOCK_STREAM, 0);

 SOCKADDR_IN client = {0,};

 client.sin_addr.s_addr = inet_addr(YOURIP);
 client.sin_family = AF_INET;
 client.sin_port = htons(YOURPORT);
 
 
 InitEvent(sock);
 
 WSAEventSelect(app.sarr[app.now],app.hEarr[app.now],FD_CONNECT);
 connect(sock,(SOCKADDR *)&client,sizeof(client));

 app.now++;

 WSANETWORKEVENTS ev;
 int index;
  
  

 while(1)
 {
  index = WSAWaitForMultipleEvents(app.now,app.hEarr,FALSE,WSA_INFINITE,FALSE);
  index -=  WSA_WAIT_EVENT_0;
  WSAEnumNetworkEvents(app.sarr[index],app.hEarr[index],&ev);

  switch(ev.lNetworkEvents)
  {
  case FD_CONNECT:
   connectProc(index);
   break;
  case FD_WRITE:
   writeProc(index);
   break;
  case FD_CLOSE:
   CloseProc(index);
   break;
  } 
 }
 

 WSACleanup();
}


void connectProc(int index)
{
 SOCKET sock = app.sarr[index];
 DeleteSocket(index);
 
 InitEvent(sock);
  
 WSAEventSelect(app.sarr[app.now], app.hEarr[app.now],FD_WRITE);
 app.now++;
}


void writeProc(int index)
{
 if(app.sarr[index])
 {
  while(1)
  {
   SendData(index);
  }
 }

}


void CloseProc(int index)
{
 WSACloseEvent(app.hEarr[index]);
 closesocket(app.sarr[index]);


 DeleteSocket(index);
}

void DeleteSocket(int index)
{
 app.now--;
 app.hEarr[index] = app.hEarr[app.now];
 app.sarr[index] = app.sarr[app.now];
}


void InitEvent(SOCKET sock)
{
 app.sarr[app.now] = sock;
 app.hEarr[app.now] = WSACreateEvent();
}


void SendData(int index)
{
 char str[100];
 printf("아무거나 입력해봐:\n");
 scanf("%s",str);

 SOCKET sock = app.sarr[index];
 send(sock,str,100,0);
}

'API & MFC > 네트워크프로그래밍' 카테고리의 다른 글

우아한 종료  (0) 2010.02.09
EventSelect(서버)  (0) 2010.02.05
EventSelect란?  (0) 2010.02.05
ASyncSelect 예제  (1) 2010.02.04
소켓통신의 라이브러리 사용 선택 시 고려사항  (0) 2010.02.02
Posted by 아몰라


#pragma comment(lib,"ws2_32")
#include <winsock2.h>
#include <iostream>

using namespace std;

struct App
{
 SOCKET sarr[WSA_MAXIMUM_WAIT_EVENTS];  //WSA_MAXIMUM_WAIT_EVENTS 는 64로 정의되어있다.
 HANDLE hEarr[WSA_MAXIMUM_WAIT_EVENTS];   //64는 한 소켓이 소유할 수 있는 이벤트 객체 핸들의 최대갯수
 HANDLE hFileArr[WSA_MAXIMUM_WAIT_EVENTS];
 int now; //신호상태인 이벤트객체의 갯수
};

App app;

void AcceptProc(int index);
void ReadProc(int index);
void CloseProc(int index);

void main()
{
 WSADATA wsadata;
 WSAStartup(MAKEWORD(2,2),&wsadata);

 SOCKET sock;
 sock = socket(AF_INET,SOCK_STREAM,0);

 SOCKADDR_IN servaddr={0,};

 servaddr.sin_family = AF_INET;
 servaddr.sin_addr.s_addr = inet_addr("192.168.34.103");
 servaddr.sin_port = htons(1100);

 int re = bind(sock,(SOCKADDR *)&servaddr,sizeof(servaddr));
 re = listen(sock,5);
 app.sarr[app.now]=sock; //셋팅된 sock을 app.sarr[app.now]에 넣어준다.
 
 
 app.hEarr[app.now] = WSACreateEvent();  //이벤트객체를 생성
 WSAEventSelect(app.sarr[app.now],app.hEarr[app.now],FD_ACCEPT); 
 //app.sarr[app.now] 에서 FD_ACCEPT가 발생하면 app.hEarr[app.now]를 신호상태로 바꿔준다.

 app.now++; //생성됐으니 카운트 증가

 WSANETWORKEVENTS ev; //이벤트의 유형정보와 오류정보로 채워질 WSANETWORKEVENTS 구조체 변수


 int index;
 while(1)
 {
  index = WSAWaitForMultipleEvents(app.now,app.hEarr,FALSE,WSA_INFINITE,FALSE);
  index -= WSA_WAIT_EVENT_0;

//FALSE 이면 이벤트 객체중에서 하나라도 신호상태이면 index를 반환해준다.
//TRUE 이면 전부 신호상태이어야 반환된다.
//2개 이상 신호상태일시 인덱스 번호가 낮은 것이 반환된다.


  WSAEnumNetworkEvents(app.sarr[index],app.hEarr[index],&ev);
//이벤트가 발생한 소켓과 신호상태인 이벤트객체핸들에서 발생한이벤트의 유형정보와 오류정보가 WSANETWORKEVENTS 구조체의 주소값으로 전달된다.

  switch(ev.lNetworkEvents)
  {
  case FD_ACCEPT:
   AcceptProc(index);
   break;
  case FD_READ:
   ReadProc(index);
   break;
  case FD_CLOSE:
   CloseProc(index);
   break;
  }  
 }
 WSACleanup();
}

void AcceptProc(int index)
{
 SOCKADDR_IN clientaddr;
 int len = sizeof(clientaddr);
 SOCKET sock = app.sarr[index];
 app.sarr[app.now] = accept(sock,(SOCKADDR *)&clientaddr,&len);
 app.hEarr[app.now] = WSACreateEvent();
 WSAEventSelect(app.sarr[app.now],app.hEarr[app.now],FD_READ|FD_CLOSE);
 app.now++;
}

void ReadProc(int index)
{
 char buf[100];
 SOCKET sock = app.sarr[index];
 if(recv(sock,buf,100,0)<=0) //접속이 종료되었거나 에러가 나면 리턴
 {
  return;  
 }
 printf("%s\n", buf);
}

void CloseProc(int index)
{
 WSACloseEvent(app.hEarr[index]);
 closesocket(app.sarr[index]);

 app.now--;
 app.hEarr[index] = app.hEarr[app.now];
 app.sarr[index] = app.sarr[app.now];
}

'API & MFC > 네트워크프로그래밍' 카테고리의 다른 글

우아한 종료  (0) 2010.02.09
EventSelect(클)  (0) 2010.02.05
EventSelect란?  (0) 2010.02.05
ASyncSelect 예제  (1) 2010.02.04
소켓통신의 라이브러리 사용 선택 시 고려사항  (0) 2010.02.02
Posted by 아몰라

1.Event Select모델 이란 ?

 

 : WSAEventSelect() 함수가 핵심적인 역할을 하는데 WSAAscyncSelect와 다르게 윈도우 메세지 형태  대신 이벤트 객체를 통해서 네트워크 이벤트를 감지 하고 이벤트 객체를 관찰함으로써 스레드를 사용하지 않고도 여러 개의 소켓을 처리 가능한 네트워크 모델.

 

 

2. 동작 원리

  : 소켓과 이벤트 객체를 연결 소켓 에 대한 이벤트를 처리하는데 ,연결 이유는 발생한 이벤트를 이벤트 객체를 통해서 감지하기 위함인데 네트워크 이벤트가 발생할 때마다 이벤트 객체의 신호 상태를 통해 네트워크 이벤트 발생을 감지할 수 있다.

 

 

 

 

 

3. 입출력 절차

 

 

  WSACreateEvent() 함수를 이용하여 이벤트 객체를 생성한           .

WSAEventSelect() 함수를 이용하여 소켓에 이벤트 객체 연결 처리할 네트워크 연결

WSAWaitForMultipleEvents() 이벤트객체가 이벤트 발생유무를 확인 하기 위한 함수(신호 감지)

WSAEnumNetworkEvents() 함수를 호출하여 발생한 네트워크 이벤트 및 오류 를알아낸다

 

 

4.관련 함수 및 예제

 

1.    WSAEventSelect() 함수

 

 #include <winsock2.h>

int WSAEventSelect(SOCKET S, WSAEVENT hEventObject, long lNetworkEvents);

성공시 0, 실패시 SOCKET_ERROR 반환

SOCKET S   :   관찰대상인 소켓의 핸들 전달.

WSAEVENT hEventObject  :  이벤트 발생유무의 확인을 위한 이벤트객체 핸들

long lNetworkEvents : 감시하고자하는 이벤트의 종류 등록

 








S
에 전달된 핸들의 소켓에서 lNetworkEvents 에 전달된 이벤트중 하나가 발생하면, hEventObject에 전달된 핸들의 커널 오브젝트를 signaled(신호) 상태로 바꾸는 함수이다.

.

이벤트는 WSAAsyncSelect 모델 하고 같다.

 

 

 

 

2.    이벤트 객체 생성과 제거: WSACreateEvent(),WSACloseEvent()

 

WSAEVENT   WSACreateEvent ( ) ;

                                   성공: 이벤트 객체 핸들 리턴

                               실패: WSA_INVALID_EVENT 리턴

BOOL  WSACloseEvent (WSAEVENT hEvent) ;

                                       성공: TRUE, 실패: FALSE

 

 

 






예제 코드

 

void Doit(intindex)

{

   SOCKADDR_IN clientaddr;

   intlen = sizeof(clientaddr);

   SOCKETsock = sarr[index];

   sarr[cnt] = accept(sock,(SOCKADDR*)&clientaddr,&len);

   hEarr[cnt] = WSACreateEvent(); //이벤트 객체 생성

   WSAEventSelect(sarr[cnt],hEarr[cnt],FD_READ|FD_CLOSE);//소켓 ,객체 연결

   cnt++;

}

 

 

 











3. 이벤트 객체의 신호 상태 감지하기 :
WSAWaitForMultipleEvents() 함수

 

DWORD WSAWaitForMultipleEvents (

   DWORD cEvents,

   const WSAEVENT* lphEvents,

   BOOL fWaitAll,

   DWORD dwTimeout,

   BOOL fAlertable

) ;

          

 

 

 





DWORD cEvents, :
이벤트 객체의 수 (하나의 소켓에 여러 개의이벤트 객체가능)

const WSAEVENT* lphEvents, : 이벤트 객체의 배열의 시작 주소(최대 64개가능)

BOOL fWaitAll,: 전체 신호 상태 / 단일 신호 상태에 따른 리턴시기를 구분

(TRUE 전달시 배열안의 이벤트 객체 가 전부 신호 상태이어야 리턴 가능)

(FALSE 전달시 배열안의 이벤트 객체가 하나라도 신호 상태이어도 리턴 가능)

DWORD dwTimeout,: 신호상태 감시할 시간 설정 (WSA_INFINITE : 무한대기)

BOOL fAlertable: 입출력 완료 시기

리턴 : 리턴값에서 WSA_WAIT_EVENT_0 인 상수를 빼면 이벤트 객체배열에서 이벤트가 발생한 이벤트핸들의 인덱스 값이 된다. 2개이상일경우 에는 인덱스가 작은 것부터 값을 얻는다.

 

 

 

 

 

4.구체적인 네트워크 이벤트 알아내기 :

int WSAEnumNetworkEvents (

   SOCKET s,

   WSAEVENT hEventObject,

   LPWSANETWORKEVENTS lpNetworkEvents

) ;                        성공: 0, 실패: SOCKET_ERROR

 

 







 

  

 SOCKET s, : 이벤트가 발생한 소켓의 핸들 전달
WSAEVENT hEventObject, :  소켓과 연결된, 신호상태인 이벤트 객체핸들  LPWSANETWORKEVENTS lpNetworkEvents : 발생한 이벤트의 유형정보와 오류정보로 채워질 WSANETWORKEVENTS 구조체 변수의 주소 값 전달

 

  

 

 

예제 코드 (WSAWaitForMultipleEvents 함수,WSAEnumNetworkEvents함수)

WSANETWORKEVENTSev;

intindex;

while(1){

   index = WSAWaitForMultipleEvents (cnt,hEarr,FALSE,WSA_INFINITE,FALSE);//이벤트유무확인

   index  = index – WSA_WAIT_EVENT_0;

   WSAEnumNetworkEvents(sarr[index],hEarr[index],&ev);//구체적인 이벤트 오류 알아내기

   switch(ev.lNetworkEvents){

   caseFD_ACCEPT:

       //do it..    break;

   caseFD_READ:

       //do it..    break;

   caseFD_CLOSE:

       //do it.     break;

 

 

'API & MFC > 네트워크프로그래밍' 카테고리의 다른 글

EventSelect(클)  (0) 2010.02.05
EventSelect(서버)  (0) 2010.02.05
ASyncSelect 예제  (1) 2010.02.04
소켓통신의 라이브러리 사용 선택 시 고려사항  (0) 2010.02.02
소켓함수 예제(로그인서버)  (0) 2010.01.25
Posted by 아몰라