본문 바로가기

High Level Technique/Window System

Anonymouse Pipe, Named Pipe

Anonymouse Pipe, Named Pipe


Windows에서 파이프는 두가지가 존재하는데 Anonymouse Pipe와 Named Pipe가 있습니다. Anonymouse Pipe는 부모 자식 관계 프로세스(혹은 자식, 자식)들 사이에서 통신하는 경우에 유용합니다. Named Pipe는 주소 정보가 존재하는 파이프인데, 주소가 있다는 것은 서로 관계가 없는 프로세스들 사이에서도 주소 정보를 공유함으로써 데이터를 주고 받을 수 있다는 것입니다.


앞서 메일슬롯과의 차이를 보자면 Named Pipe는 양뱡향 통신이 가능하다는 것이고 MailSlot은 브로드케스트 방식(단방향)의 데이터 전송이 가능합니다.




AnonymousePipe



AnonymousePipe.cpp


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
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
 
int main(void)
{
    HANDLE hReadPipe, hWritePipe;
 
    TCHAR sendString[] = _T("Anonymouse Pipe");
    TCHAR recvString[100];
 
    DWORD bytesWritten;
    DWORD bytesRead;
 
    CreatePipe(&hReadPipe, &hWritePipe, NULL0);
 
    WriteFile(hWritePipe, sendString, lstrlen(sendString)*sizeof(TCHAR), &bytesWritten, NULL);
 
    _tprintf(_T("string Send: %s \n"), sendString);
 
    ReadFile(hReadPipe, recvString, bytesWritten, &bytesRead, NULL);
 
    recvString[bytesRead / sizeof(TCHAR)] = 0;
    _tprintf(_T("String Recv: %s \n"), recvString);
 
    CloseHandle(hReadPipe);
    CloseHandle(hWritePipe);
 
    return 0;
}
 
cs








NamedPipe


NamedPipe는 양뱡향 통신이 핵심이다. 예를 들어 서버와 클라이언트가 있다고 하자. 서버에서는 CreateNamedPipe()를 이용해 Pipe를 생성하고 ConnectNamedPipe()를 이용해 연결이 되도록 대기를 한다.  그러면 클라이언트에서 서버로 연결을 해야하는데 이때 CreateFile()을 사용한다.



NamedPipe_Server.cpp


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
 
#define BUF_SIZE 2014
int CommToClient(HANDLE);
 
int _tmain(int argc, TCHAR* argv[])
{
    LPTSTR pipeName = _T("\\\\.\\pipe\\simple_pipe");
    HANDLE hPipe;
 
    while (1)
    {
        hPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUF_SIZE, BUF_SIZE, 20000NULL);
 
        if (hPipe == INVALID_HANDLE_VALUE)
        {
            printf("CreatePipe Failed\n");
            return -1;
        }
 
        BOOL isSuccess = 0;
        isSuccess = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
 
        if (isSuccess)
        {
            CommToClient(hPipe);
        }
        else
        {
            CloseHandle(hPipe);
        }
 
        return 1;
    }
}
 
int CommToClient(HANDLE hPipe)
{
    TCHAR fileName[MAX_PATH];
    TCHAR dataBuf[BUF_SIZE];
 
    BOOL isSuccess;
    DWORD fileNameSize;
    isSuccess = ReadFile(hPipe, fileName, MAX_PATH*sizeof(TCHAR), &fileNameSize, NULL);
 
    if (!isSuccess || fileNameSize == 0)
    {
        printf("Pipe read message error! \n");
        return -1;
    }
 
    FILE* filePtr = _wfopen(fileName, _T("r, ccs=UNICODE"));
    
    if (filePtr == NULL)
    {
        printf("%d\n", GetLastError());
        _tprintf(_T("File open fault! \n"));
        return -1;
    }
 
    DWORD bytesWritten = 0;
    DWORD bytesRead = 0;
 
    while (!feof(filePtr))
    {
        bytesRead = (fread(dataBuf, sizeof(TCHAR), BUF_SIZE, filePtr))*sizeof(TCHAR);
 
        WriteFile(hPipe, dataBuf, bytesRead, &bytesWritten, NULL);
 
        if (bytesRead != bytesWritten)
        {
            printf("Pipe write message error! \n");
            break;
        }
    }
 
    FlushFileBuffers(hPipe);
    DisconnectNamedPipe(hPipe);
    CloseHandle(hPipe);
 
    return 1;
}
cs


15라인을 보면 CreateNamedPipe()에 3번째 인자를 보면 OR 연산을 하는 것을 볼 수 있다. 이 3번째 인자는 데이터 전송타입, 데이터 수신 타입, 블로킹 모드를 설정한다.


단순히 문자열을 주고 받는 것이 목적이라면 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIP_WAIT 를 사용하면 되고, 

바이너리 데이터를 주고 받는 것이 목적이라면 PUPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIP_WAIT 로 하면 된다.




NamedPipe_Cilent.cpp


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
 
#define BUF_SIZE 1024
 
int _tmain(int argc, TCHAR* argv[])
{
    HANDLE hPipe;
    TCHAR readDataBuf[BUF_SIZE + 1];
    LPTSTR pipeName = _T("\\\\.\\pipe\\simple_pipe");
 
    while (1)
    {
        hPipe = CreateFile(pipeName, GENERIC_READ | GENERIC_WRITE, 0NULL, OPEN_EXISTING, 0NULL);
 
        if (hPipe != INVALID_HANDLE_VALUE)
        {
            break;
        }
 
        if (GetLastError() != ERROR_PIPE_BUSY)
        {
            printf("Could not open pipe \n");
            return 0;
        }
 
        if (!WaitNamedPipe(pipeName, 20000))
        {
            printf("Could not open pipe \n");
            return 0;
        }
    }
 
    DWORD pipeMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
    BOOL isSuccess = SetNamedPipeHandleState(hPipe, &pipeMode, NULLNULL);
 
    if (!isSuccess)
    {
        printf("SetNamedPipeHAndleSate failed\n");
        return 0;
    }
 
    LPCTSTR fileName = _T("C:\\Users\\k3y6reak\\Documents\\Visual Studio 2013\\Projects\\NamedPipe_Client\\Debug\\message.txt");
    DWORD bytesWritten = 0;
 
    isSuccess = WriteFile(hPipe, fileName, (lstrlen(fileName) + 1* sizeof(TCHAR), &bytesWritten, NULL);
    if (!isSuccess)
    {
        printf("WriteFile failed \n");
        return 0;
    }
 
    DWORD bytesRead = 0;
    
    while (1)
    {
        isSuccess = ReadFile(hPipe, readDataBuf, BUF_SIZE*sizeof(TCHAR), &bytesRead, NULL);
 
        if (!isSuccess && GetLastError() != ERROR_MORE_DATA)
        {
            break;
        }
 
        readDataBuf[bytesRead/sizeof(TCHAR)] = 0;
        _tprintf(_T("%s \n"), readDataBuf);
    }
 
    CloseHandle(hPipe);
 
    return 0;
}
cs


실행결과는 아래와 같다.



책의 예제에는 단순히 file명만 가지고 실행하는데 정상적으로 작동하지 않는다. Debugging을 해서 보면 분명이 파일명은 넘어오는데 File Pointer값이 NULL로 되어 읽어드리지를 못한다.

그래서 절대경로를 넣어주면 잘 실행된다.