티스토리 뷰

190704-3
파이프

교육을 받으면서 노트필기 했던 내용을 날것 그대로 업로드합니다.


 
IPC - 파이프
 
  • 단방향 파이프
  • 양방향 파이프
 
  • 이름있는 파이프 : 이름으로 언제든지 커널오브젝트 핸들을 얻을 수 있다.
  • 이름없는 파이프
 
지금 실습은 양방향, 이름있는 파이프
 
Client : 요청
Server : 응답
 
우리 예제는 이름있는 양방향 파이프 이면서 서버와 클라이언트 개념이 존재한다. (멀티 파이프)
파이프 서버가 mp라고 알려주면 클라이언트가 파이프를 만들어 달라고 요청한다. -> 파이프가 만들어지고 파이프의 한쪽을 핸들로 클라이언트가 갖는다. 서버쪽도 파이프의 한쪽의 핸들은 서버가 갖는다. 라고 생각하자. -> 클라이언트에서 먼저 발송 -> 서버가 받는다. -> 다시 클라이언트에 전송
 
다시. 클라이언트2번이 mp라는 이름으로 통신한다면 또 독립적인 파이프가 만들어진다. 한쪽 핸들은 클라이언트2번 나머지 쪽 핸들은 서버가. 
 
 
서버
//윈도우 API 정복 김상형저 소스
 
#include <windows.h>
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
HWND hWndMain;
LPCTSTR lpszClass = TEXT("MultiPipe1");
 
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
    , LPSTR lpszCmdParam, int nCmdShow)
{
    기본 코드 생략
}
 
// 차일드의 요청을 처리한다.
DWORD WINAPI PipeThread(LPVOID temp)
{
    HANDLE hPipe = (HANDLE)temp;
    TCHAR szInput[255],/*애플리케이션 버퍼*/ szOutput[255];
    DWORD dwRead, dwWritten;
    BOOL bSuc;
 
    // 요청을 받고 연산을 한 후 응답을 보낸다.
    for (;;) { //*주고받기
        bSuc = ReadFile(hPipe, szInput/*이 버퍼에 받는다.*/, 255, &dwRead, NULL);
        //* ReadFile : 데이터를 읽는 함수. 또한 블록함수다. 읽을 때 까지 블록
        if ((bSuc == FALSE) || (dwRead == 0)) { // 클라이언트가 통신을 종료했을 경우
            break;
        }
 
        if (lstrcmp(szInput, "one") == 0) lstrcpy(szOutput, "하나");
        if (lstrcmp(szInput, "two") == 0) lstrcpy(szOutput, "둘");
        if (lstrcmp(szInput, "three") == 0) lstrcpy(szOutput, "셋");
        if (lstrcmp(szInput, "four") == 0) lstrcpy(szOutput, "넷");
        if (lstrcmp(szInput, "five") == 0) lstrcpy(szOutput, "다섯");
 
        bSuc = WriteFile(hPipe, szOutput, lstrlen(szOutput) + 1, &dwWritten, NULL);
        // * WriteFile : 이 또한 블럭함수. 모두 데이터를 전송할 때까지 반환하지 않는다. // 클라이언트가 안받아도 파이프에 박어놓기만 해도되서 ReadFile보다 대부분 빠르다.
        if ((bSuc == FALSE) || (dwWritten == 0)) { // 클라이언트가 통신을 종료했을 경우
            break;
        }
    }
 
    // 파이프 해제
    FlushFileBuffers(hPipe); // 파이프의 출력 버퍼가 빌 때까지 대기 * 버퍼에있는 데이터를 밀어서 없앤다(또는받는다)
    DisconnectNamedPipe(hPipe); // 파이프 연결이 끊기고 클라이언트가 에러를 인식한다.
    CloseHandle(hPipe); // 파이프 인스턴스를 닫는다.
    return 0;
}
 
// 클라이언트 하나당 하나씩의 스레드를 만든다.
DWORD WINAPI MainThread(LPVOID temp)
{
    HANDLE hPipe;
    DWORD ThreadID;
    BOOL bCon;
 
    for (;;) {
        // 파이프 생성
        hPipe = CreateNamedPipe( //*이름있는 파이프를 생성한다.
            "\\\\.\\pipe\\MultiPipe", //파이프 이름 * 이름을 알아야 접속요청을 할 수 있다. 접속요청을 받아들였을 때 하고하하는 동작을 수행할 수 있다.
            // "\\\\"는 ms가 만들어놓은 프로토콜 상이라는 뜻이다.
            // "." 은 현재 컴퓨터
            // "MultiPipe" : 파이프의 이름이다.
            PIPE_ACCESS_DUPLEX, // Read, Write 모두 * 양방향 가능한 파이프로 생성
            PIPE_TYPE_BYTE, //바이트 단위 입출력 or PIPE_TYPE_MESSAGE(파일의 바이너리, 텍스트 모드와 같다)
            3, // 최대 인스턴스 3개 *파이프가 3개 인스턴스를 생성할 수 있다.
            4096, //출력버퍼 크기(지정할뿐 시스템이 크기 설정)
            4096, //입력버퍼 크기(0 디폴트 크기)
            20000, //WaitNamedPipe함수의 만료 시간(밀리세컨드 단위)을 서버에서 시장한다. 클라이언트는 NMPWAIT_USE_DEFAULT_WAIT로 타임아웃을 지정한다.
            NULL //보안속성
        );// 이미 3개 만들어져있을 경우 -1을 반환하여 효력이 없다.
 
        // 차일드 대기. 접속되면 차일드 요청 처리 스레드 생성
        bCon = ConnectNamedPipe(hPipe, NULL); //* 대기함수 : 호출시 클라이언트가 접속하는지 대기한다. (블락상태)
        if ((bCon == FALSE) && (GetLastError() == ERROR_PIPE_CONNECTED))
            bCon = TRUE;
        if (bCon == TRUE) { // TRUE면 잘 접속한 것이다.
            CloseHandle(CreateThread(NULL, 0, PipeThread, (LPVOID)hPipe, 0, &ThreadID));//클라언트가 접속했다면 파이프쓰레드를 하나 생성한다.
        }
        else {
            CloseHandle(hPipe);
        }
    }
    return 0;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    DWORD ThreadID;
    TCHAR* Mes = "클라이언트로부터의 접속을 대기중이며 요청을 처리합니다";
 
    switch (iMessage) {
    case WM_CREATE:
        CloseHandle(CreateThread(NULL, 0, MainThread, NULL, 0, &ThreadID));
        //* MainThread 라는 함수를 실행하는 쓰레드를 생성한다.
        //* 파이프 클라이언트가 파이프의 이름으로 접속하는지 대기하기 위해서 만든 쓰레드
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        TextOut(hdc, 50, 50, Mes, lstrlen(Mes));
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
 
 
클라이언트
#include <windows.h>
 
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
HWND hWndMain;
LPCTSTR lpszClass=TEXT("MultiPipe2");
 
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
     ,LPSTR lpszCmdParam,int nCmdShow)
{
   기본 코드 생략
}
 
 
TCHAR Mes[255]="파이프 서버를 실행시켜 주십시오";
#define random(i) (rand() % i) // i만큼 숫자가 발생한다.
 
// 서버로 요청을 보내 결과를 받아온다.
DWORD WINAPI MainThread(LPVOID temp)
{
    HANDLE hPipe;
    TCHAR szInput[255], szOutput[255];
    DWORD dwRead, dwWritten;
    TCHAR arInput[5][10]={"one","two","three","four","five"};
    BOOL bSuc;
 
    // 서버가 파이프를 생성할 때까지 무한 대기한다.
    for (;;) {
        if (WaitNamedPipe("\\\\.\\pipe\\MultiPipe",NMPWAIT_WAIT_FOREVER)==TRUE) {
            //WaitNamedPipe : 인스턴스가 남지 않는다면 Wait하는 함수(우리가 정해준 개수는 최대 3개)
            hPipe=CreateFile("\\\\.\\pipe\\MultiPipe",GENERIC_READ | GENERIC_WRITE,
                0,NULL,OPEN_EXISTING/*존재할 때만 접속요청*/,0,NULL); // 파이프를 연결하는 함수. 만들어놓은 파이프를 연결한다.
            if (hPipe!=INVALID_HANDLE_VALUE)
                break;
        }
    }
 
    for (;;) {
        // one~five중 하나를 난수로 선택한다.
        lstrcpy(szInput,arInput[random(5)]);
 
        // 요청을 보낸 후 응답을 받는다.
        bSuc=WriteFile(hPipe,szInput,lstrlen(szInput)+1,&dwWritten,NULL); // * 요청을 보낸다
        if ((bSuc==FALSE) || (dwWritten==0))
            break;
        ReadFile(hPipe,szOutput,255,&dwRead,NULL); // 응답을 읽어온다
 
        // 응답을 화면으로 출력한다.
        wsprintf(Mes,"%s=%s",szInput,szOutput);
        InvalidateRect(hWndMain,NULL,TRUE);
        UpdateWindow(hWndMain);
 
        Sleep(500);
    }
 
    // 서버가 먼저 종료된 경우
    lstrcpy(Mes,"서버가 종료되었습니다.");
    InvalidateRect(hWndMain,NULL,TRUE);
 
    // 파이프를 해제하고 결과를 메인 윈도우에 출력한다.
    CloseHandle(hPipe);
    
    return 0;
}
 
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    DWORD ThreadID;
 
    switch (iMessage) {
    case WM_CREATE:
        hWndMain=hWnd;
        srand(GetTickCount()); // 랜드함수 씨드값 초기화한다.
        CloseHandle(CreateThread(NULL,0,MainThread,NULL,0,&ThreadID)); // 쓰레드 생성한다.
        return 0;
    case WM_PAINT:
        hdc=BeginPaint(hWnd, &ps);
        TextOut(hdc,50,50,Mes,lstrlen(Mes)); // 문자열 출력한다.
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
 
댓글
댓글쓰기 폼
네이버 이웃추가
«   2019/10   »
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
글 보관함