2013年6月4日 星期二

Named Pipes的實作

[概述]

簡單來說就是一個windows幫你建好、已經可以用的queue,可以塞資料進去然後依序再把它拿出來,也可以支援雙面塞入和取出(雙插頭?!)。它的好處在於可以同時一直拿資料也可以一直塞資料,所以通常可用在不同Thread做資料傳送使用。

[架構]

主要分為Server端和Client端,下面實作是由Server端從Pipe收資料,Client端則是把資料塞到Pipe裡
我們Server端的步驟是:
1. 創建一個Name pipe
2. 跟Client做connect
3. 開始從Pipe讀資料出來
4. 收完資料以後要記得Disconnect和關Handle

Client端步驟則為:
1. 與伺服器作connect動作
2. 將Server端創好的Name pipe開起來(如開檔案)
3. 寫寫寫,寫東西到Pipe
4. 寫完收工關Handle

[實作]

Server端:

以\.\Pipe\Test當Name為例,m_hPipe是成員變數

步驟1.
m_hPipe = CreateNamedPipe("\\\\.\\Pipe\\Test", PIPE_ACCESS_DUPLEX,
                           PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
                           1, 1000, 1000, 1000, NULL);    // 創建Named Pipe
if (m_hPipe == INVALID_HANDLE_VALUE)
    ::AfxMessageBox(_T("Create Named Pipe fail."));

步驟2.
由於接下來要做的Connect步驟會一直等待Client端的連線,所以從這裡之後必須另外開一個Thread來執行此部分,所以另外創建一個ServerProc
if ( !ConnectNamedPipe(pView->m_hPipe, NULL))             // 等待Client Connect
{
    CloseHandle(pView->m_hPipe);                          // 失敗要關閉Handle
    ::AfxMessageBox(_T("Connect Named Pipe fail."));
    return false;
}

步驟3.
Connect上以後就是開始做讀取的動作了,讀取動作跟讀檔案是完全一樣的,
阿要讀幾次就看個人,看你是要寫迴圈一直讀還是啥都可以,不過要記得跳離開就是了。
這裡範例就只讀一次這樣。

char strBuffer[1024] = {0};
DWORD ReadNum;
if ( !ReadFile(pView->m_hPipe, strBuffer, sizeof(strBuffer), &ReadNum, NULL))
{
    CloseHandle(pView->m_hPipe);                          // 失敗要關閉Handle
    ::AfxMessageBox(_T("Read Pipe fail."));
}

CString CStrTemp;
CStrTemp = strBuffer;
::AfxMessageBox(CStrTemp);

步驟4.
打完收工要記得Disconnect和關閉Handle,不然會挫賽
if (DisconnectNamedPipe(pView->m_hPipe))                  // 終止Server連線
{
    CloseHandle(pView->m_hPipe);                          // 關閉Pipe
    pView->m_hPipe = NULL;
}
else
    ::AfxMessageBox(_T("Disconnect Named Pipe fail"));

return true;

Client端:

Client端的部分可以開個Thread去丟資料,總之不要跟Server是同Thread就是了,這樣才不會Block住。
步驟1.
跟Server connect的方式是使用WaitNamedPipe
注意的是,這個動作要比Server端的ConnectNamedPipe執行才可以,不然會錯亂。

if ( !WaitNamedPipe("\\\\.\\Pipe\\Test", NMPWAIT_WAIT_FOREVER)) // 等待Server連線
{
    ::AfxMessageBox(_T("Wait Pipe Fail."));
    return false;
}

步驟2.
使用CreateFile(開檔案的方式)來創建一個Handle供後來寫入Pipe使用

HANDLE hPipe = CreateFile("\\\\.\\Pipe\\Test", GENERIC_WRITE, 0, NULL,
                           OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hPipe == INVALID_HANDLE_VALUE)
{
    ::AfxMessageBox(_T("Open Pipe Fail."));
    return false;
}

步驟3.
透過剛剛創建的Handle將資料寫入Pipe
寫入的方式也是使用寫檔的WriteFile來實現

CString CStrMessage = _T("Test message.");                // 測試傳送的字串
DWORD WriteNum;
if( !WriteFile(hPipe, CStrMessage , CStrMessage .GetLength(), &WriteNum, NULL))
{
    ::AfxMessageBox(_T("Write Message to Pipe Fail."));
    return false;
}

步驟4.
打完收工記得關Handle

CloseHandle(hPipe);                                       // 關閉handle

就介紹到這裡囉~下回見

沒有留言:

張貼留言