1. 程式人生 > >windows程式設計之命名管道通訊

windows程式設計之命名管道通訊

管道分為兩種,一種是匿名管道,一種是命名管道。兩者都可以進行程序間的通訊,但匿名管道有侷限性,它只能在本機上使用,而不能跨網路使用。但是命名管道就不一樣了,它彌補了匿名管道的侷限性。接下來看下,如何在windows平臺下,進行命名管道的通訊。

首先,要進行兩個進行間的通訊,必須要有一個程序主動一點,來建立一個命名的管道,呼叫CreateNamedPipe即可建立一個命名的管道,其宣告如下:

HANDLE CreateNamedPipe(
  LPCTSTR lpName,                             // 管道名稱,形式必須為\\.\pipe\pipeName
  DWORD dwOpenMode,                           // 開啟管道的模式
  DWORD dwPipeMode,                           // 管道的模式,傳輸資料的形式
  DWORD nMaxInstances,                        // 最大連線客戶端的個數
  DWORD nOutBufferSize,                       // 輸出緩衝區的大小
  DWORD nInBufferSize,                        // 輸入緩衝區的大小
  DWORD nDefaultTimeOut,                      // 預設的超時時間
  LPSECURITY_ATTRIBUTES lpSecurityAttributes  // 安全屬性,一般為NULL
);

建立管道的程序,我們稱之為伺服器,當我們建立完管道之後,伺服器得知道什麼時候有客戶端進行連線,我們可以通過一個OVERLAPPED這個結構,該結構裡有一個event事件,當有客戶端進行連線時,事件物件就變成有訊號。有了事件之後,我們就可以呼叫ConnectNamedPipe來等待一個客戶端的連線,其宣告如下:

BOOL ConnectNamedPipe(  
  HANDLE hNamedPipe,          // 命名管道物件
  LPOVERLAPPED lpOverlapped   // OVERLAPPED結構
);
客戶端連線之後,兩者之間就可以 進行通訊了,通訊的操作跟我們的檔案操作是一樣的,通過ReadFile和WriteFile來進行讀和寫。

通訊完之後呢,我們可以呼叫DisconnectNamedPipe來進行斷開連線,其宣告如下:

BOOL DisconnectNamedPipe(  
  HANDLE hNamedPipe   // 命名管道物件
);
以下是服務端的測試程式碼:
#include <windows.h>
#include <stdio.h>

int main(int argc, char ** argv)
{
	//建立一個命名管道,在windows中\代表zhuan'yi兩個\\代表一個\
	HANDLE hNamedPipe = CreateNamedPipeA("\\\\.\\pipe\\testName",
		PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
		PIPE_TYPE_BYTE, 1, 1024, 1024,0 , NULL);
	//檢查是否建立成功
	if (hNamedPipe == INVALID_HANDLE_VALUE)
	{
		printf("create named pipe failed!\n");
	}
	else
	{
		printf("create named pipe success!\n");
	}
	//非同步IO結構
	OVERLAPPED op;
	ZeroMemory(&op, sizeof(OVERLAPPED));
	//建立一個事件核心物件
	op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	//等待一個客戶端進行連線
	BOOL b = ConnectNamedPipe(hNamedPipe, &op);
	//當有客戶端進行連線時,事件變成有訊號的狀態
	if (WaitForSingleObject(op.hEvent, INFINITE) == 0)
	{
		printf("client connect success!\n");
	}
	else
	{
		printf("client connect failed!\n");
	}
	//連線成功後,進行通訊,讀寫
	char  buff[100];
	sprintf_s(buff, 100, "test message from server!");
	DWORD cbWrite;
	WriteFile(hNamedPipe, buff, strlen(buff), &cbWrite, NULL);

	ZeroMemory(buff, 100);
	ReadFile(hNamedPipe, buff, 100, &cbWrite, NULL);
	//通訊完之後,斷開連線
	DisconnectNamedPipe(hNamedPipe);
	//關閉管道
	CloseHandle(hNamedPipe);
	system("pause");
	return 0;
}
當服務端將管道建立好了之後,客戶端就不需要再次建立管道了,客戶端只需要連線管道即可,但在連線之前,我們應該呼叫WaitNamedPipe來檢查一下,命名管道是否存在,其宣告如下:
BOOL WaitNamedPipe(  
  LPCTSTR lpNamedPipeName,  // 管道名稱,形式必須為<span style="font-family: Arial, Helvetica, sans-serif;">\\.\pipe\pipeName</span>
  DWORD nTimeOut            // 超時時間,給NULL為預設的超時時間
);
當檢查到命名管道存在了且可用,我們就可以連線管道,連線管道很簡單,就是呼叫CreateFile來開啟命名管道。開啟之後就可以進行通訊了,就是一些檔案操作,

以下是客戶端的測試程式碼:

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

int main(int argc, char ** argv)
{
	//檢查命名管道是否存在
	BOOL b = WaitNamedPipeA("\\\\.\\pipe\\testName", 0);
	//開啟管道
	HANDLE hFile = CreateFileA("\\\\.\\pipe\\testName",
		GENERIC_READ | GENERIC_WRITE,
		0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	//檢查是否連線成功
	if (!b || hFile == INVALID_HANDLE_VALUE)
	{
		printf("connect failed!\n");
	}
	else
	{
		printf("connect success!\n");
	}
	//進行通訊
	char  buf[100];
	ZeroMemory(buf, 100);
	DWORD dwRead;
	ReadFile(hFile, buf, 100, &dwRead, NULL);
	printf(buf);
	WriteFile(hFile, "test message for client!", strlen("test message for client!"), &dwRead, NULL);
	//關閉管道
	CloseHandle(hFile);
	system("pause");
	return 0;
}