728x90

 

IPC의 특성

- 메일 슬롯

    -> 방향성 : 단방향, 브로드캐스팅

    -> 통신범위 : 제한 없음

- 이름없는 파이프

    -> 방향성 : 단방향

    -> 통신범위 : 부모-자식 프로세스 간

- 이름있는 파이프

    -> 방향성 : 양방향

    -> 통신범위 : 제한 없음

 

이름없는 파이프

- 파이프의 핸들 값을 통해 읽기, 쓰기에 접근

- 부모-자식 프로세스 간 통신이 가능

    -> 부모 프로세스에서 생성한 파이프의 핸들 값이 자식 프로세스로 상속이 가능하기 때문

- 파이프 생성 : CreatePipe()

BOOL CreatePipe(
  PHANDLE               hReadPipe,
  PHANDLE               hWritePipe,
  LPSECURITY_ATTRIBUTES lpPipeAttributes,
  DWORD                 nSize
);

HANDLE hReadPipe, hWritePipe;

CreatePipe(
	&hReadPipe, 	// 파이프의 출구(읽기)에 접근할 수 있는 핸들
	&hWirtePipe,	// 파이프의 입구(쓰기)에 접근할 수 있는 핸들 
	NULL, 0);

- 읽기, 쓰기 : WriteFile(), ReadFile()

ReadFile(
    hReadPipe,
    recvString,
    byteWirtten,
    &bytesRead,
    NULL
);

WriteFile(
    hWritePipe,
    sendString,
    lstrlen(sendString)*sizeof(TCHAR),
    &byteWritten,
    NULL
);

 

이름 있는 파이프

- server 측(파이프 생성하는 쪽)

    -> CreateNamedPipe() : 파이프 생성

    -> ConnectNamedPipe() : 파이프를 연결 대기 상태로 전환

// namedpipe_server.cpp

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

#define BUF_SIZE 1024

int CommToClinet(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,	// 최대 인스턴스 갯수 = 보유 가능 파이프 갯수
										// createpipe 처음 호출할때만 설정이 됨
										// 다음에 호출할 때도 유지하는 것이 좋음
			BUF_SIZE,					// 출력 버퍼 사이즈
			BUF_SIZE,					// 입력 버퍼 사이즈
			20000,						// 클라이언트 타임-아웃
										// 연결 요청을 기다리는 시간(ms)
			NULL						// 보안 설정
		);
		if (hPipe == INVALID_HANDLE_VALUE)   
		{
			_tprintf(_T("Create pipe failed"));
			return -1;
		}

		BOOL isSuccess = 0;
		isSuccess = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

		if (isSuccess)
			CommToClinet(hPipe);   
		else
			CloseHandle(hPipe);
	} // 여러 클라이언트가 순차적으로 접속하기 위해 무한 루프
	return 1;
}

int CommToClinet(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)
	{
		_tprintf(_T("Pipe read message error! \n"));
		return -1;
	}

	FILE* filePtr = _tfopen(fileName, _T("r"));	// 수신된 데이터의 파일을 열기

	if (filePtr == NULL)
	{
		_tprintf(_T("File open error!!\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) 
		{
			_tprintf(_T("Pipe write message error!!!\n"));
			break;
		}
	} // open 한 파일의 데이터를 읽어와서 클라이언트에 데이터 송신

	FlushFileBuffers(hPipe);       // 버퍼를 비워줌     
	DisconnectNamedPipe(hPipe);    // 파이프를 소멸
	CloseHandle(hPipe);                
	return 1;
}

- client 측(파이프에 접속하려는 쪽)

    -> CreateFile() : 호출에 의한 파이프 연결(오픈)

// namedpipe_client.cpp

#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,	// 읽기 쓰기 모드 설정
			0,
			NULL,
			OPEN_EXISTING,
			0,
			NULL
		);
		if (hPipe != INVALID_HANDLE_VALUE)    
			break;	// 연결이 되었다면 while을 빠져나옴

		if (GetLastError() != ERROR_PIPE_BUSY)   
		{
			_tprintf(_T("Could not open pipe\n"));
			return 0;
		} // 연결 요청 시간이 지난 것을 확인

		if (!WaitNamedPipe(pipeName, 20000))      
		{
			_tprintf(_T("Could not open pipe\n"));
			return 0;
		} // 설정한 연결 요청 시간동안 대기
		  // 시간 설정을 안하면 서버에서 설정한 시간만큼 대기
	} // 서버에 연결을 요청하기 위한 무한 루프

	DWORD pipeMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;  // 읽는 모드를 메시지 기반 모드 설정
	BOOL isSuccess = SetNamedPipeHandleState(            
		hPipe,		// 파이프 핸들
		&pipeMode,	// 변경할 파이프 모드 
		NULL,
		NULL
	); // 위에 설정한 모드대로 변경

	if (!isSuccess)
	{
		_tprintf(_T("SetNamedPipeHandleState failed!\n"));
		return 0;
	}

	LPTSTR fileName = _T("news.txt");
	DWORD bytesWritten = 0;
	isSuccess = WriteFile(                              
		hPipe,			// 파이프 핸들
		fileName,		// 전송할 메시지                  
		(_tcslen(fileName) + 1) * sizeof(TCHAR),	// 메시지 길이
		&bytesWritten,	// 전송된 바이트 수
		NULL
	); // 데이터 송신
	if (!isSuccess)
	{
		_tprintf(_T("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;
}

- 서버 프로젝트 폴더 안에 news.txt가 있어야 함

- client 에서 보낸 파일명을 받아서 그 파일명의 파일을 열어 내용을 다시 클라이언트에 전송

728x90

+ Recent posts