'overlapped'에 해당되는 글 2건

  1. 2015.06.12 Named Pipe Server Using Overlapped I/O and Client
  2. 2015.06.11 Windows Named Pipe 구현 간단 정리
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

// NamedPipeServer.h

#pragma once


namespace EventType

{

enum EventType

{

Exit,

Connect,

Max

};

}


class NamedPipeServer

{

public:

NamedPipeServer(LPCWSTR name);

~NamedPipeServer();


BOOL Start();

VOID Stop();


private:

std::wstring m_strName;

std::wstring m_strBuffer;

BOOL m_isStopped;

HANDLE m_hEvent[EventType::Max];

};


// NamedPipeServer.cpp

#include "stdafx.h"

#include "NamedPipeServer.h"


NamedPipeServer::NamedPipeServer(LPCWSTR name) :

m_strName(name),

m_strBuffer(),

m_isStopped(FALSE)

{

}


NamedPipeServer::~NamedPipeServer()

{

for (int i = 0; i < EventType::Max; ++i)

CloseHandle(m_hEvent[i]);

}


BOOL NamedPipeServer::Start()

{

OVERLAPPED oOverlapped;

HANDLE hPipe = NULL;

WCHAR buffer[MAX_BUFFER_SIZE] = { 0, };

DWORD dwRead;


while (m_isStopped == FALSE)

{

hPipe = CreateNamedPipe(m_strName.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, MAX_BUFFER_SIZE * sizeof(WCHAR), MAX_BUFFER_SIZE * sizeof(WCHAR), 0, NULL);

if (hPipe == INVALID_HANDLE_VALUE)

return FALSE;


for (int i = 0; i < EventType::Max; ++i)

{

m_hEvent[i] = CreateEvent(NULL, TRUE, FALSE, NULL);

if (m_hEvent[i] == NULL)

return FALSE;

}


ZeroMemory(&oOverlapped, sizeof(OVERLAPPED));

oOverlapped.hEvent = m_hEvent[EventType::Connect];


if (ConnectNamedPipe(hPipe, &oOverlapped) == 0)

{

if (GetLastError() != ERROR_IO_PENDING)

{

CloseHandle(hPipe);

return FALSE;

}

}


DWORD dwEvent = WaitForMultipleObjects(EventType::Max, m_hEvent, FALSE, INFINITE);


switch (dwEvent)

{

case EventType::Exit:

break;


case EventType::Connect:

ReadFile(hPipe, buffer, MAX_BUFFER_SIZE * sizeof(WCHAR), &dwRead, NULL);

if (0 < dwRead)

m_strBuffer = buffer;

break;

}


for (int i = 0; i < EventType::Max; ++i)

CloseHandle(m_hEvent[i]);


DisconnectNamedPipe(hPipe);

CloseHandle(hPipe);

}


return TRUE;

}


VOID NamedPipeServer::Stop()

{

m_isStopped = TRUE;


SetEvent(m_hEvent[EventType::Exit]);

}


// NamedPipeClient.h

#pragma once


class NamedPipeClient

{

public:

NamedPipeClient();

~NamedPipeClient();


BOOL Open(LPCWSTR name);

BOOL Send(LPCWSTR message);


private:

HANDLE m_hPipe;

};


// NamedPipeClient.cpp

#include "stdafx.h"

#include "NamedPipeClient.h"


NamedPipeClient::NamedPipeClient() : m_hPipe(NULL)

{

}


NamedPipeClient::~NamedPipeClient()

{

CloseHandle(m_hPipe);

}


BOOL NamedPipeClient::Open(LPCWSTR name)

{

SetLastError(ERROR_SUCCESS);


// Try to open a named pipe; wait for it, if necessary.

for (int retry = 0; retry < 3; ++retry)

{

m_hPipe = CreateFile(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);


// Break if the pipe handle is valid.

if (m_hPipe != INVALID_HANDLE_VALUE)

break;


// Exit if an error other than ERROR_PIPE_BUSY occurs. 

if (GetLastError() != ERROR_PIPE_BUSY)

return FALSE;


// All pipe instances are busy, so wait for 180 seconds.

if (WaitNamedPipe(name, 180000) == FALSE)

return FALSE;

}


return TRUE;

}


BOOL NamedPipeClient::Send(LPCWSTR message)

{

DWORD dwNumberOfBytesToWrite = (wcslen(message) + 1) * sizeof(WCHAR);

DWORD dwNumberOfBytesWritten;


return WriteFile(m_hPipe, message, dwNumberOfBytesToWrite, &dwNumberOfBytesWritten, NULL);

}


// main.cpp - Server

int main()

{

NamedPipeServer* pServer = new NamedPipeServer(L"\\\\.\\pipe\\PMAPipe");

std::thread pipe([=]

{

pServer->Start();

});


// for stop test

Sleep(300000);


pServer->Stop();


pipe.join();


if (pServer != NULL)

{

delete pServer;

pServer = NULL;

}


return EXIT_SUCCESS;

}


// main.cpp - Client

int main()

{

NamedPipeClient* pClient = new NamedPipeClient();

pClient->Open(L"\\\\.\\pipe\\PMAPipe");

pClient->Send(L"FUCK THAT SHIT");


if (pClient != NULL)

{

delete pClient;

pClient = NULL;

}


return 0;

}



'Programming > C / C++' 카테고리의 다른 글

int64_t 값 출력하기  (0) 2015.08.15
to read UTF-8 XML using TinyXML  (0) 2015.08.06
Windows Named Pipe 구현 간단 정리  (0) 2015.06.11
GetSystemMetrics  (0) 2015.06.04
Singleton  (0) 2015.06.02
Posted by 역시인생한방
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Windows에서 Named Pipe를 실제로 사용하기 위해서는 Read/Write를 비동기적으로 처리해야 하는데, 이를 위해 흔히 사용되는 방법은 Overlapped I/O와 Event를 기반으로 하는 것입니다.

 

다시 말해서, 파이프를 생성하고 초기화한 후 파이프의 핸들과 함께 ReadFile/WriteFile API를 호출시, 무조건 데이터를 가지고 리턴하는게 아니라 호출 순간에 처리가 완료되었든 그렇지 않든 관계없이 바로 다음 줄로 넘어 가버립니다. (=비동기적) 그러면 당연히 개발자 입장에선 Read, Write가 완료되는 시점이 궁금하겠죠. 이 시점을 알아내는 방식이 여러가지가 있는데, 명명된 파이프 입출력 자체가 윈도의 Overlapped I/O에 해당되기 때문에 Overlapped I/O와 연계가 가능한 Event Object를 쓰겠다는 의미입니다.

 

ReadFile/WriteFile API의 arguments를 보면 Overlapped I/O를 위한 OVERLAPPED 구조체를 입력받는 항목이 있는데, OVERLAPPED 구조체의 내부에는 Event Object를 입력받는 변수가 있습니다. 이 변수에 CreateEvent를 이용해서 생성한 Event Object의 HANDLE을 넘기면 ReadFile/WriteFile이 완료되는 순간 해당 Event Object가 호출되는 것이죠. (참고로, ReadFile/WriteFile의 리턴값이 TRUE일 경우에는 처리해야 될 데이터의 양이 적어 즉시 끝난 것을 의미하지만, lpNumberOfBytes... 인수에 NULL을 준 경우에 한해 Event Object가 다시 한번 호출됩니다. 문제는 이 부분에 대한 MSDN의 설명이 완전히 막장 그 자체입니다 (...) 모순이 여기저기서 발생하는데, 제가 보기엔 TRUE인 경우에도 MSDN의 설명과 각종 예제 코드와는 달리 Pending으로 처리해야 하지 않나 싶습니다.)

 

그렇다면 구체적으로 어떻게 구현해야 할지가 문제가 되는데, MSDN에 Overlapped I/O와 Event를 결합하여 Pipe Server를 구현한 예제가 있습니다. n개의 Pipe에 대해 n개의 Event를 생성한 후, WaitForMultipleObjects에 이벤트의 배열을 넣고 무한루프를 돌리는 방식입니다. 하지만 이 예제에는 몇 가지 부족한 점이 있는데, 그중 한 가지는 Event가 하나만 존재하여 Connect/Read/Write 중 어느 사건에 대한 이벤트가 발생한 것인지 구분이 불가능하기 때문에 Connect->Read->Write->Close의 고정된 순서로 구현되어 원하는 임의의 시점에 실시간으로 Read / Write를 할 수가 없다는 것이고, 나머지 하나는 파이프를 위한 별도의 Thread가 존재하지 않아 전제 프로그램의 로직이 파이프의 실행만을 위해 사용되어야 한다는 것입니다. 그리고 댓글에서 제기된 문제점 하나를 추가하자면 Pipe와 관련된 데이터를 담는 struct가 초기화되지 않아서 일부 시스템에서 실행이 안 된다고 하는데, 0으로 초기화하면 해결됩니다.

참고로, MSDN의 예제에 있는 ReadFile / WriteFile에는 lpNumberOfBytesRead / lpNumberOfBytesWritten을 받게 되어있는데, 지속적으로 입출력을 할 경우엔 이렇게 하면 안되고 NULL을 준 다음 GetOverlappedResult에서 받아야 합니다. 이 부분에 대한 자세한 사항은 ReadFile / WriteFile의 MSDN에 일부 언급되어 있습니다.

 

따라서 실제 구현에서는 별도의 Thread를 생성하고 해당 Thread 안에서 Events에 대해 WaitForMultipleObjects를 실행하되, 파이프당 Connect, Read, Write, Update 총 4개의 Event를 등록하여 실시간으로 클라이언트와 데이터를 주고받을 수 있게 하면 됩니다. 이를 위해서는 Connect/Read/Write를 위한 3개의 OVERLAPPED 구조체와, 임의의 시점에 파이프에 뭔가를 쓰기 위해 WaitForMultipleObjects를 리턴시키는 Update Event를 등록하여, ConnectNamedPipe API에 Connect OVERLAPPED 객체를 등록하고, ReadFile과 WriteFile에 각각 Read/Write OVERLAPPED 객체를 등록하여 사용하게 됩니다. (Event가 3개로 늘어났기 때문에 Pending인지 나타내는 변수도 1개인 MSDN의 예제와 달리 Connect, Read, Write의 3개가  필요합니다.)

 

그리고 Read/Write를 위한 두개의 별도의 큐를 생성하고, Pipe server thread와 Main thread간의 동기화를 위한 두 개의 Critical Section Object 또한 생성하여 연계시킨 뒤, Pipe에서 Read Event가 발생시 Main thread를 위해 Read queue에 데이터를 읽어들인 이후 PostThreadMessage 등을 이용해 전달하고, 반대로 Main thread에서 파이프에 데이터를 쓸 경우 Write queue에 데이터를 넣은 다음 Update Event를 호출하는 식으로 하면 실시간 처리가 가능합니다. 이외에 큐를 초기화하기 위한 Reset Event 같은 것을 하나 더 등록해도 되겠죠.

 

아, 그리고 마지막으로 주의해야 할 것은 Pipe가 놀면 안된다는 겁니다. 일단 파이프가 연결된 상태가 됐을 경우, 항상 ReadFile로 읽고 있지 않으면 상대쪽 파이프가 끊겼을 때 WaitForMultipleObjects가 리턴하지 못하고 데드락이 걸리게 됩니다.

 

요약하자면, 위와 같이 구현했을 때 Main thread 입장에서 Pipe class를 보면 Init, Read, Write, Reset 정도의 메소드가 노출되고, 파이프의 읽기 이벤트는 PostThreadMessage로 전달받게 됩니다. 그리고 Pipe thread에서는 [이벤트 수신 대기 (WaitForMultipleObjects) -> 비동기 처리 결과 확인 (GetOverlappedResult) -> ReadFile/WriteFile 호출 및 큐 관련 처리] 정도로 무한루프를 돌게 되겠죠.

이상은 Named Pipe Server에 대한 설명이고, 클라이언트의 경우도 크게 다르지 않습니다.

 

(Pipe Server의 규모가 클 경우엔, Event 대신에 대규모 비동기 입출력에 높은 효율을 발휘하는 IOCP를 이용하는 방법도 있습니다.)


출처 : http://jscript.blog.me/220134379022

'Programming > C / C++' 카테고리의 다른 글

to read UTF-8 XML using TinyXML  (0) 2015.08.06
Named Pipe Server Using Overlapped I/O and Client  (0) 2015.06.12
GetSystemMetrics  (0) 2015.06.04
Singleton  (0) 2015.06.02
URLDownloadToFile example  (0) 2015.05.26
Posted by 역시인생한방
,