- 이벤트 모델
- 1_event// 현재 프로젝트는 GUI 프로그램이 아니라고 링커에게 알려주는것 #pragma comment(linker, "/subsystem:console") #include <stdio.h> #include <Windows.h> #include <conio.h> // 윈도우 "event"개념은 linux 의 "conditional variable"가 거의 // 유사합니다. // 실제로 포팅할때 서로 대응되는 개념입니다. DWORD __stdcall foo( void* p ) { HANDLE h = (HANDLE)p; // 이벤트이 signal 상태 조사하기 DWORD ret = WaitForSingleObject( h, 0); if ( ret == WAIT_OBJECT_0) printf("시그널 상태(1) 입니다.\n"); else printf("시그널이 아닙니다.\n"); // 시그널 될때까지 대기하는 방법 ret = WaitForSingleObject( h, INFINITE); // 시간(무한) if ( ret == WAIT_OBJECT_0) printf("시그널 상태(1) 입니다.\n"); else printf("시그널이 아닙니다.\n"); return 0; } int main() { HANDLE hEvent = CreateEvent( 0, 0, FALSE, // 초기 signal을 0); // 0으로 CreateThread( 0, 0, foo, (void*)hEvent, 0, 0); getch(); SetEvent( hEvent); // event의 signal을 1로 변경한다. getch(); }
- 2_event
■ WSAAsyncSelect
: non-blocking, 비동기 통지방식 (통지 방식으로 윈도우 메시지를 사용하므로 윈도우 프로시저에서만 사용할 수 있다.)
: 커널에게 미리 등록만 해두면 유저는 따로 커널에게 확인하여 동기화하지 않더라도 알아서 메시지가 날아온다.
: 내부적으로 따로 체크하는 것이 아니라 운영체제가 I/O 상황이 될때 인터럽트를 사용하는 방식으로 구현되기 때문에 운영체제 수준에서도 연산량이 많이 줄어든다. 대신 다른 운영체제에서 지원하지 않는 기능이기 때문에, 다른 구동환경에서 같은 프로세스를 사용할 수가 없다는 단점은 있다.
■ WSAEventSelect
: 이 통지방식은 다소 애매한 구석이 있다. 우선 이벤트 오브젝트를 사용하여 signal을 체크한다는 점이 동기랑 비슷한 점이 있어보인다. 그리고 wait함수로 이벤트가 발생할 때까지 대기한다는점이 blocking같기도 하다. 하지만 select나 epoll처럼 유저가 리소스를 사용하여 체크하는 것이 아니라 Wait함수를 사용하여 이벤트가 발생할 때 활성화 된다는 점이 비동기 방식에 가깝다고 생각한다.
: WSAWaitForMultipleEvent() 함수에서 timeout 옵션이 있기 때문에 select나 epoll 처럼 어떻게 사용하느냐에 따라 blocking 방식으로 사용할 수도 non-blocking 방식으로 사용할 수도 있다.
: 하지만 다른 점이 있다면, blocking 방식을 사용해도 멀티플렉싱이 가능하다는 점이다. 하나의 Wait에서 여러개의 I/O를 동시에 감지하고 있기 때문에, 쓰레드 하나만 wait를 통해 blocking한 상태에서 대기시키면 멀티플렉싱이 가능하다. 결과적으로는 Blocking 이면서 Non-Blocking이기도 하다.#pragma comment(linker,"/subsystem:console") #define WIN32_LEAN_AND_MEAN #include <stdio.h> #include <WinSock2.h> #include <Windows.h> #include <conio.h> #pragma comment(lib, "ws2_32.lib") int main() { WSADATA w; int ret = WSAStartup( MAKEWORD(2,2), &w); int listen_sock = socket( PF_INET, SOCK_STREAM, 0); SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_port = htons(4000); addr.sin_addr.s_addr = INADDR_ANY; bind( listen_sock, (struct sockaddr*)&addr, sizeof addr); listen(listen_sock, 5); //------------------ WSAEVENT ev = WSACreateEvent(); // CreateEvent()의 네트워크 // 버전. // 소켓을 비동기 소켓으로 변경한다. WSAEventSelect( listen_sock, ev, FD_ACCEPT); //----------------------------- // 모든 소켓 핸들과 이벤트 핸들을 배열에 보관해야 합니다. int sockArr[256] = { listen_sock }; WSAEVENT evArr[256] = { ev }; int cnt = 1; while( 1 ) { int pos = WSAWaitForMultipleEvents( cnt, evArr, // event배열이름 FALSE, // 하나라도 signal 되면 WSA_INFINITE,0); // 배열의 몇번째 요소인지 조사한다 int idx = pos - WSA_WAIT_EVENT_0; printf("%d 번째 이벤트가 signal\n", idx); WSANETWORKEVENTS netEv; WSAEnumNetworkEvents( sockArr[idx], evArr[idx], &netEv); if ( netEv.lNetworkEvents & FD_READ ) { char s[1024] = {0}; int n = recv( sockArr[idx], s, 1024, 0); printf("도착한 data : %s\n", s); strcat( s, " from Server"); send( sockArr[idx], s, 1024, 0); } if ( netEv.lNetworkEvents & FD_CLOSE ) { closesocket( sockArr[idx]); WSACloseEvent( evArr[idx]); // 배열에서 제거 해야 합니다. sockArr[idx] = sockArr[cnt-1]; evArr[idx] = evArr[cnt-1]; --cnt; printf("Client 접속 끊어짐\n"); continue; } if ( netEv.lNetworkEvents & FD_ACCEPT) { printf("접속요청\n"); SOCKADDR_IN caddr; int sz = sizeof caddr; int link_sock = accept( listen_sock, (SOCKADDR*)&caddr, &sz); WSAEVENT ev2 = WSACreateEvent(); WSAEventSelect( link_sock, ev2, FD_READ | FD_CLOSE); sockArr[cnt] = link_sock; evArr[cnt] = ev2; ++cnt; printf("클라이언트 접속 : %s\n", inet_ntoa( caddr.sin_addr)); } } //------------------------------ WSACleanup(); }
Overlapped IO
: 중첩 입출력 모델
: non-blocking, asynchronous I/O (비동기적 완료 통보)
: 데이터 입출력이 진행되는 동안에도 다른일을 할 수 있다.
: 원리는 리눅스의 I/O multiplexing, RTS과 비슷하다.
: 리눅스의 I/O multiplexing, RTS와 다른점은 모드 변환이 발생하지 않는다. (user <-> kernel 여러번 데이터 복사 x)#pragma comment(linker, "/subsystem:console") #define WIN32_LEAN_AND_MEAN #include <stdio.h> #include <WinSock2.h> #include <Windows.h> #pragma comment(lib, "ws2_32.lib") int main() { WSADATA w; int ret = WSAStartup( MAKEWORD(2,2), &w); //------------------------------------------- // 1. Overlapped io를 위한 소켓 생성 // 표준 C 네트워크 함수 : 소문자() // WSAxxxx() 함수들 : windows socket 2.0 부터 지원되는 // windows만의 개념들.. int listen_sock = WSASocket( PF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED); // 2. 소켓에 주소 지정(bind) SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_port = htons(4000); addr.sin_addr.s_addr = INADDR_ANY; bind( listen_sock, (struct sockaddr*)&addr, sizeof addr); listen(listen_sock, 5); struct sockaddr_in addr2; int sz = sizeof addr2; int link_sock = accept( listen_sock, (struct sockaddr*)&addr2, &sz); printf("클라이언트가 접속되었습니다\n"); closesocket(listen_sock); // 대기 소켓은 이제 필요없다. // Overlapped로 수신 하는 코드 WSAEVENT ev = WSACreateEvent(); WSAOVERLAPPED ov = {0}; ov.hEvent = ev; // 수신 버퍼.. char s[1024] = {0}; WSABUF buf; buf.buf = s; buf.len = 1024; DWORD flag = 0; DWORD recvBytes = 0; int n = WSARecv( link_sock, &buf, 1, // 버퍼와 버퍼 갯수 &recvBytes, // 받을 data 크기 &flag, &ov, // overlapped 구조체 0); if ( n == SOCKET_ERROR ) // -1 { if ( WSAGetLastError() == WSA_IO_PENDING ) { printf("data 를 수신하고 있는 중입니다.\n"); // 비동기의 종료를 대기 해야 합니다. // 비동기 IO의 종료시에는 overlapped구조체 안에 있는 // event가 signal 됩니다. WSAWaitForMultipleEvents( 1, &ev, TRUE, WSA_INFINITE, 0); printf("비동기 IO로 수신 완료\n"); } } else printf("data를 동기적으로 수신 : %d\n", n); printf("수신된 data : %s\n", buf.buf); closesocket( link_sock); //------------------------------ WSACleanup(); }
참고: http://ozt88.tistory.com/22
'Programing > Server Model' 카테고리의 다른 글
서버 모델 - 패킷(Protocol Buffers, Snappy) (0) | 2016.02.28 |
---|---|
서버 모델 - 윈도우 IOCP (0) | 2016.02.26 |
서버 모델 - 윈도우 소켓 프로그래밍 (0) | 2016.02.26 |
서버 모델 - Event Driven I/O (0) | 2016.02.22 |
서버 모델 - I/O multiplexing (0) | 2016.02.22 |