命名管道學習(簡單例程)----Windows程序間通訊
管道(Pipe)實際是用於程序間通訊的一段共享記憶體,建立管道的程序稱為管道伺服器,連線到一個管道的程序為管道客戶機。通常我們把管道分為匿名管道和命名管道。但對於匿名管道的話,只能在本機上程序之間通訊,而且只能實現本地的父子程序之間的通訊,侷限性太大了。而這裡介紹的命名管道,就和匿名管道有些不同了,在功能上也就顯得強大許多,至少其可以實現跨網路之間的程序的通訊,同時其客戶端既可以接收資料也可以傳送資料,伺服器端也是可以接收資料,又可以傳送資料。
命名管道(NamedPipes)是在管道伺服器和一臺或多臺管道客戶機之間進行單向或雙向通訊的一種命名的管道。一個命名管道的所有例項共享同一個管道名,但是每一個例項均擁有獨立的快取與控制代碼,並且為客戶——服務通訊提供有一個分離的管道。例項的使用保證了多個管道客戶能夠在同一時間使用同一個命名管道。
學習補充資料:孫鑫MFC筆記教程(17)--程序間通訊2(命名管道)
https://blog.csdn.net/liufei_learning/article/details/5060281
命名管道的使用步驟
伺服器端:
1):伺服器程序呼叫CreateNamedPipe函式來建立一個有名稱的命名管道在建立命名管道的時候必須指定一個本地的命名管道名稱。windows允許同一個本地的命名管道名稱右多個命名管道例項。所以,伺服器程序在呼叫CreateNamedPipe函式時必須指定最大允許的例項數(0-255).如果CreateNamedPipe函式成功返回後,伺服器程序得到一個指向一個命名管道例項的控制代碼。
HANDLE CreateNamedPipe( LPCTSTR lpName, // pipe name DWORD dwOpenMode, // pipe open mode DWORD dwPipeMode, // pipe-specific modes DWORD nMaxInstances, // maximum number of instances DWORD nOutBufferSize, // output buffer size DWORD nInBufferSize, // input buffer size DWORD nDefaultTimeOut, // time-out interval LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD );
該函式用來建立一個命名管道的例項,並返回這個命名管道的控制代碼。如果需要建立一個命名管道的多個例項,就需要多次呼叫CreateNamedPipe函式,引數 lpName 為一個字串,其格式必須為 \\.\pipe\pipeName,其中圓點 ”.” 表示的是本地機器,如果想要與遠端的伺服器建立連線,那麼這個圓點位置處應指定這個遠端伺服器的名稱,而其中的 “pipe” 這個是個固定的字串,也就是說不能進行改變的,最後的 “pipename” 則代表的是我將要建立的命名管道的名稱了,引數 dwOpenMode 用來指定管道的訪問方式,重疊方式,寫直通方式,還有管道控制代碼的安全訪問方式。
2):伺服器程序就可以呼叫ConnectNamedPipe來等待客戶的連線請求,這個ConnectNamedPipe既支援同步形式,又支援非同步形式,若伺服器程序以同步形式呼叫 ConnectNamedPipe函式,如果沒有得到客戶端的連線請求,則會一直等到客戶端的連線請求。當該函式返回時,客戶端和伺服器之間的命名管道連線已經建立起來了
BOOL ConnectNamedPipe(
HANDLE hNamedPipe, // handle to named pipe
LPOVERLAPPED lpOverlapped // overlapped structure
);
該函式的作用是讓伺服器等待客戶端的連線請求的到來 hNamedPipe 指向一個命名管道例項的伺服器的控制代碼
pOverlapped 指向一個 OVERLAPPED結構的指標,如果 hNamedPipe 所標識的命名管道是用 FILE_FLAG_OVERLAPPED,
(也就是重疊模式或者說非同步方式)標記開啟的,則這個引數不能為 NULL ,必須是一個有效的指向一個 OVERLAPPED 結構的指標,否則該函式可能會錯誤的執行。
3):這個時候伺服器端就可以向客戶端讀(ReadFile)/寫(WriteFile)資料了。
BOOL ReadFile(
HANDLE hFile, // handle to file
LPVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // number of bytes read
LPOVERLAPPED lpOverlapped // overlapped buffer
);
4):在已經建立連線的命名管道例項中,伺服器程序就會得到一個指向該管道例項的控制代碼,這個控制代碼稱之為伺服器端控制代碼,同時服務端程序可以呼叫DisconnectNamedPipe函式,將一個管道例項與當前建立連線的客戶端程序斷開,從而可以重新連線到新的客戶端程序。當然,伺服器也可以呼叫CloseHandle來關閉一個已經建立連線的命名管道例項。
BOOL DisconnectNamedPipe( HANDLE hNamedPipe // handle to named pipe);
伺服器端程式:
#include <windows.h>
#include <stdio.h>
void main(void)
{
HANDLE PipeHandle;
DWORD BytesRead;
CHAR buffer[1024] = {0};
if ((PipeHandle = CreateNamedPipe("\\\\.\\Pipe\\mypipe",PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE,1,0,0,1000,NULL)) == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe failed with error %x \n",GetLastError());
return;
}
printf("Server is now running \n");
if (ConnectNamedPipe(PipeHandle,NULL) == 0)
{
printf("ConnectNamePipe failed with error %x \n",GetLastError());
CloseHandle(PipeHandle);
return;
}
printf("ConnectNamedPipe成功!\n");
if(ReadFile(PipeHandle,buffer,sizeof(buffer),&BytesRead,NULL) <= 0)
{
printf("ReadFile failed with error %x \n",GetLastError());
CloseHandle(PipeHandle);
return;
}
printf("byteread = %d,buffer = %s \n",BytesRead,buffer);
if (DisconnectNamedPipe(PipeHandle) == 0)
{
printf("DisconnectNamedPipe failed with error %x \n",GetLastError());
return;
}
CloseHandle(PipeHandle);
system("pause");
}
客戶端:
1):在這裡客戶端也可以先呼叫WaitNamedPipe函式來測試指定名稱的管道例項是否可用。在已經建立的命名管道例項中,客戶端程序就會得到一個指向該管道例項的控制代碼。這個控制代碼稱之為客戶端控制代碼。
BOOL WaitNamedPipe(
LPCTSTR lpNamedPipeName, // pipe name
DWORD nTimeOut // time-out interval
);
通過該函式可以判斷是否有可用的命名管道。直到等待的時間間隔已過,或者指定的命名管道的例項可以用來連線了。lpNamedPipeName 用來指定命名管道的名稱,格式同CreateNamedPipe函式的lpNamedPipeName引數。
nTimeOut 用來指定超時間隔,引數可以填寫系列的值::
NMPWAIT_USE_DEFAULT_WAIT::超時間隔即為伺服器端建立該命名管道時指定的超時間隔。NMPWAIT_USE_DEFAULT_WAIT::一直等待,直到出現一個可用的命名管道的例項。
2):客戶端程序呼叫CreateFile函式連線到一個正在等待連線的命名管道上。在這裡客戶端需要指定將要連線的命名管道上。當CreateFile成功返回之後,客戶端就得到了一個指向已經建立連線的命名管道例項的控制代碼。
HANDLE CreateFile(
LPCTSTR lpFileName, // file name
DWORD dwDesiredAccess, // access mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to template file
);
3):這個時候客戶端就可以向伺服器讀(ReadFile)/寫(WriteFile)資料了.
BOOL WriteFile(
HANDLE hFile, // handle to file
LPCVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToWrite, // number of bytes to write
LPDWORD lpNumberOfBytesWritten, // number of bytes written
LPOVERLAPPED lpOverlapped // overlapped buffer
);
4):客戶端可以呼叫CloseHandle來關閉一個已經建立連線的命名管道例項。
BOOL CloseHandle( HANDLE hObject // handle to object);
客戶端程式:
#include <windows.h>
#include <stdio.h>
#include <string>
using namespace std;
void main(void)
{
HANDLE PipeHandle;
DWORD BytesWritten;
char abc[20] = "This is a test!";
if (WaitNamedPipe("\\\\.\\Pipe\\mypipe", NMPWAIT_WAIT_FOREVER) == 0)
{
printf("CreateNamedPipe failed with error %x \n",GetLastError());
return;
}
printf("Waitnamedpipe正確!\n");
if ((PipeHandle = CreateFile("\\\\.\\Pipe\\mypipe",GENERIC_READ | GENERIC_WRITE,
0 ,(LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) == INVALID_HANDLE_VALUE)
{
printf("CreateFile failed with error %x \n",GetLastError());
return;
}
printf("CreateFile成功!\n");
if(WriteFile(PipeHandle,abc, 25, &BytesWritten, NULL) == 0)
{
printf("WriteFile failed with error %x \n",GetLastError());
CloseHandle(PipeHandle);
return;
}
printf("WriteFile成功!\n");
printf("Wrote %d bytes \n", BytesWritten);
CloseHandle(PipeHandle);
system("pause");
}
執行結果(VC++6.0):