다루는 내용
Windows System에서는 로컬 망에서 IPC(Inter Process Communication)를 할 수 있게 파이프를 제공합니다.
1. CreateNamedPipe
2. ConnectNamedPipe
3. 서버 코드 설명
4. 클라이언트 코드 설명
설명에 사용할 코드
두 개의 콘솔 응용 프로그램으로 서버와 클라이언트가 있습니다.
서버는 파이프를 생성하고 클라이언트가 연결하면 메시지를 수신하여 화면에 출력합니다.
클라이언트는 사용자가 입력한 메시지를 파이프를 통해 전송합니다.
서버 코드
//나는 서버
#include <Windows.h>
#include <stdio.h>
#define PNAME TEXT("\\\\.\\pipe\\test")
void DoIt(HANDLE hPipe)
{
char buf[256];
BOOL check;
while (1)
{
check = ReadFile(hPipe, buf, 256, 0, 0);
if ((check == FALSE) ||(strcmp(buf, "exit") == 0))
{
break;
}
printf("%s\n", buf);
}
printf("연결을 닫습니다.\n");
CloseHandle(hPipe);
}
int main()
{
HANDLE hPipe;
BOOL check;
while (1)
{
hPipe = CreateNamedPipe(PNAME, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE,
1, 4096, 4096, 0, 0);
check = ConnectNamedPipe(hPipe, 0);
if ((check == false) && (GetLastError() == ERROR_PIPE_CONNECTED))
{
check = true;
}
if (check)
{
DoIt(hPipe);
}
}
return 0;
}
클라이언트 코드
//나는 클라이언트
#include <Windows.h>
#include <stdio.h>
#include <string.h>
#define PNAME TEXT("\\\\.\\pipe\\test")
int main()
{
HANDLE hPipe = CreateFile(PNAME, GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, OPEN_EXISTING,0 ,0);
if (hPipe == INVALID_HANDLE_VALUE)
{
printf("연결 실패\n");
return 0;
}
printf("서버에 연결하였습니다.\n");
while (1)
{
printf("메시지(종료:exit):");
char buf[256];
scanf_s("%s", buf, 256);
WriteFile(hPipe, buf, 256, 0, 0);
printf("전송하였습니다.\n");
if (strcmp(buf, "exit") == 0)
{
break;
}
}
CloseHandle(hPipe);
return 0;
}
1. CreateNamedPipe
이름 있는 파이프를 생성하는 함수입니다.
HANDLE
WINAPI
CreateNamedPipe(
LPCWSTR lpName,
DWORD dwOpenMode,
DWORD dwPipeMode,
DWORD nMaxInstances,
DWORD nOutBufferSize,
DWORD nInBufferSize,
DWORD nDefaultTimeOut,
LPSECURITY_ATTRIBUTES lpSecurityAttributes
); msdn 바로가기
반환 값
이름 있는 파이프 instance 핸들을 반환합니다.
실패하면 INVALID_HANDLE_VALUE를 반환합니다.
LPCWSTR lpName
파이프 이름입니다.
파이프 이름은 \\호스트이름\pipe\파이프이름입니다.
로컬 호스트는 .으로 표시합니다.
\\.\pipe\파이프이름
C언어와 C++언어에서 문자열로 나타낼 때는 “\\\\.\\pipe\\파이프이름”처럼 표현해야 합니다.
DWORD dwOpenMode
파이프 열기 모드로 액세스 모드라고 볼 수 있습니다.
수신 목적이면 PIPE_ACCESS_INBOUND를 사용합니다.
송신 목적이면 PIPE_ACCESS_OUTBOUND를 사용합니다.
송수신을 모두 하려면 PIPC_ACCESS_DUPLEX를 사용합니다.
#define PIPE_ACCESS_INBOUND 0x00000001
#define PIPE_ACCESS_OUTBOUND 0x00000002
#define PIPE_ACCESS_DUPLEX 0x00000003
DWORD dwPipeMode
파이프 모드입니다.
바이트 스트림 방식으로 통신하려면 PIPE_TYPE_BYTE를 사용합니다.
메시지 단위로 통신하려면 PIPE_TYPE_MESSAGE를 사용합니다.
#define PIPE_TYPE_BYTE 0x00000000
#define PIPE_TYPE_MESSAGE 0x00000004
DWORD nMaxInstances
허용하는 최대 인스턴스 수입니다.
DWORD nOutBufferSize
송신 버퍼 사이즈입니다.
DWORD nInBufferSize
수신 버퍼 사이즈입니다.
DWORD nDefaultTimeOut
디폴트 대기 시간입니다.
클라이언트에서 파이프에 연결 대기할 때 WaitNamedPipe 함수 호출 시에 두 번째 인자를 NMPWAIT_USE_DEFAULT_WAIT를 줄 때의 디폴트 대기 시간입니다.
LPSECURITY_ATTRIBUTES lpSecurityAttributes
보안 설명자입니다.
NULL을 전달하면 기본 보안 설명자를 사용합니다.
2. ConnectNamedPipe
서버 측에서 클라이언트의 연결을 대기하는 함수입니다.
BOOL
WINAPI
ConnectNamedPipe(
HANDLE hNamedPipe,
LPOVERLAPPED lpOverlapped
);
반환 값
성공하면 0이 아닙니다.
실패하면 0입니다.
만약 CreateNamePipe 함수 호출과 ConnectNamePipe 함수 호출 사이에 클라이언트 요청이 오면 0을 반환합니다. 이 때는 GetLastError 함수 호출의 반환 값이 ERROR_PIPE_CONNECTED입니다.
LPOVERLAPPED lpOverlapped
OVERLAPPED 구조체의 포인터입니다.
비동기 호출에 사용합니다.
*비동기보다 non-blocking이라고 말하는 것이 좀 더 정확한 표현일 수 있습니다.*
3. 서버 코드 설명
서버와 클라이언트에서 공통으로 사용할 파이프 이름을 결정합니다.
#define PNAME TEXT("\\\\.\\pipe\\test")
DoIt 함수
클라이언트와 연결 상태의 파이프에서 메시지를 수신하는 함수입니다.
파이프의 입출력은 파일 입출력 함수를 사용합니다.
따라서 수신할 때 ReadFile 함수를 사용합니다.
여기에서는 수신한 메시지가 “exit”이 올 때까지 메시지를 수신하고 화면에 출력합니다.
void DoIt(HANDLE hPipe)
{
char buf[256];
BOOL check;
while (1)
{
check = ReadFile(hPipe, buf, 256, 0, 0);
if ((check == FALSE) ||(strcmp(buf, "exit") == 0))
{
break;
}
printf("%s\n", buf);
}
printf("연결을 닫습니다.\n");
CloseHandle(hPipe);
}
main 진입점 함수
서버에서는 다음의 작업을 반복합니다.
파이프를 생성합니다.
클라이언트 연결을 대기합니다.
클라이언트 연결 대기가 성공하면 통신을 수행합니다.
int main()
{
HANDLE hPipe;
BOOL check;
while (1)
{
hPipe = CreateNamedPipe(PNAME, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE,
1, 4096, 4096, 0, 0);
check = ConnectNamedPipe(hPipe, 0);
if ((check == false) && (GetLastError() == ERROR_PIPE_CONNECTED))
{
check = true;
}
if (check)
{
DoIt(hPipe);
}
}
return 0;
}
4. 클라이언트 코드 설명
클라이언트에서 파이프에 연결하는 것은 단지 약속한 파이프 이름으로 파일 열기를 하는 것입니다.
연결 성공하면 다음을 반복합니다.
메시지 입력합니다.
메시지를 전송합니다.
입력한 메시지가 “exit”이면 반복문을 탈출합니다.
int main()
{
HANDLE hPipe = CreateFile(PNAME, GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, OPEN_EXISTING,0 ,0);
if (hPipe == INVALID_HANDLE_VALUE)
{
printf("연결 실패\n");
return 0;
}
printf("서버에 연결하였습니다.\n");
while (1)
{
printf("메시지(종료:exit):");
char buf[256];
scanf_s("%s", buf, 256);
WriteFile(hPipe, buf, 256, 0, 0);
printf("전송하였습니다.\n");
if (strcmp(buf, "exit") == 0)
{
break;
}
}
CloseHandle(hPipe);
return 0;
}