实验四:使用命名管道实现进程通信
一、实验目的
(1)了解windows系统环境下的进程通信机制。
(2)熟悉Windows系统提供的进程通信API。
(3)熟悉掌握文件读写函数WriteFile()和ReadFile()。
二、实验准备
(1).CreateNamePipe()创建一个命名管道实例,并返回该管道的句柄。
1.函数原型:
HANDLE CreateNamePipe(
LPCTSTR lpName, //命名管道的名字
DWORD dwOpenMode, //命名管道的访问模式
DWORD dwPipeMode, //命名管道的模式
DWORD nMaxInstances, //可创建实例的最大值
DWORD nOutBufferSize, //以字节为单位的输出缓冲区的大小
DWORD nInBufferSize, //以字节为单位的输入缓冲区的大小
DWORD nDefaultTimeOut, //默认超时时间
LPSECURITY_ATTRIBUTES lpSecurityAttributes //安全属性
);
2.参数说明:
(1)lpName:为命名管道的名字,管道的命名方式为\\ServerName\pipe\pipename,其中ServerName为用命名管道通信时服务器的主机名或IP地址,pipename为命名管道的名字,用户可自行定义。
(2)dwOpenMode:指出命名管道的访问模式。
PIPE_ACCESS_DUPLEX 双向管道。服务器和客户都可以进行读和写
(3)dwPipeMode:指出管道的模式。
PIPE_TYPE_BYTE 以字符流的方式向管道写数据。该模式下不能使用PIPE_READMODE_MESSAGE
PIPE_TYPE_MESSAGE 以信息流的方式向管道写数据。模式下可以使用PIPE_READMODE_MESSAGE和 PIPE_READMODE_BYTE
(4)nMaxInstances:该命名管道可以创建实例的最大值。
(5)nOutBufferSize:输出缓冲区的大小,以字节为单位。
(6)nInBufferSize:输入缓冲区的大小,以字节为单位。
(7)nDefaultTimeOut:默认的超时时间,以ms为单位。
如果函数WaitNamePipe()指出NMWAIT_USE_DEFAULT_WAIT,每个管道实例必须指定同一值的名字。
(8)lpSecurityAttributes:为管道指定安全属性,为NULL时,管道得到一个默认的安全描述符。
3.返回值:
如果管道创建成功,将返回服务器命名管道实例的句柄。如果失败,返回INVALID_HANDLE_VALUE,可以调用函数GetLastError()查询失败的原因。
(2).ConnectNamePipe()连接命名管道。创建后命名管道也等待客户端的连接,客户端可以使用函数CreateFile()和 CallNamedPipe()进行连接。
1.函数原型:
BOOL ConnectNamedPipe(
HANDLE hNamePipe, //命名管道实例句柄
LPOVERLAPPED lpOver lapped //指向Overlapped结构的指针
);
2.参数说明:
hNamedPipe:为命名管道创建时得到的一个命名管道实例句柄。
lpOverlapped:指向Overlapped结构的指针,可设其为NULL。
3.返回值:
成功,将返回一个非0值;失败,系统返回0,可以调用函数GetLastError()查询失败的原因。
(3).DisconnecrNamePipe()拆除命名管道服务器与客户端的连接。
1.函数原型:
BOOL DisconnectNamePipe(
HANDLE hNamePipe //命名管道实例句柄
);
2.参数说明:
hNamedPipe:为命名管道创建时得到的一个命名管道实例句柄。
3.返回值:
成功,将返回一个非0值;失败,系统返回0,可以调用函数GerLasrError()查询失败的原因。
(4).CallNamePipe()客户端连接服务器建立的命名管道。
1.函数原型:
BOOL CallNamePipe(
LPCTSTR lpNamePipeName, //命名管道的名字
LPVOID lpInBuffer, //输出数据缓冲区
DWORD nInBufferSize, //以字节为单位的输出数据缓冲区的大小
LPVOID lpOurBuffer, //输入数据缓冲区指针
DWORD nOurBufferSize, //以字节为单位的输入数据缓冲区的大小
LPDWORD lpBytesRead, //输入字节数指针
DWORD nTimeOUT //等待时间
);
2.参数说明:
lpNamedPipeName:命名管道的名字。
lpInBuffer:指出用于输出数据(向管道写数据)的缓冲区指针。
nInBufferSize:用于输出数据缓冲区的大小,以字节为单位。
lpOutBuffer:指出用于接收数据(从管道读出数据)的缓冲区指针。
nOutBufferSize:指向用于接收数据缓冲区的大小,以字节为单位。
lpBytesRead:一个32位的变量,改变量用于存储从管道读出的字节数。
nTimeOut:等待命名管道成为可用状态的时间,单位为ms。
3.返回值:
成功,将返回一个非0值;失败,系统返回0,可用调用函数GetLastError()查询失败的原因。
(5).WairNamedPipe()客户端等待服务器连接命名管道。
1.函数原型:
BOOL WaitNamedPipe(
LPCTSTR lpNamedPipeName, //要等待的命名管道名
DWORD nTimeOut //等待时间
);
2.参数说明:
lpNamedPipeName:要等待的命名管道的名字。
nTimeOut:等待命名管道成为可用状态的时间,单位为ms.
3.返回值:
在等待时间内要连接的命名管道可以使用,将返回一个非0值。在等待时间内要连接的命名管道不可以使用,系统返回0,可用调用GetLastError()查询失败的原因。
三、实验内容
(一)实验内容
实验:
使用命名管道完成两个进程之间的通信。要求使用Windows系统提供的命名管道完成两个进程之间的通信,要求能正确使用创建命名管道CreateNamePipe()、连接命名管道ConnectNamePipe()、拆除命名道的连接DisconnectNamePipe()、连接服务器已建立的命名管道CallNamePipe()、等待命名管道WaitNamedPipe()等API。
(二)主要代码
服务器程序:
#include "stdafx.h"
#include "04.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// The one and only application object
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
int err;
bool rc;
HANDLE hPipeHandle1;
char lpName[] = "\\\\.\\pipe\\myPipe";
char InBuffer[50] = "";
char OutBuffer[50] = "";
DWORD BytesRead, BytesWrite;
//创建一个命名管道
hPipeHandle1 = CreateNamedPipe((LPCTSTR)lpName,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC ,
PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 20, 30,
NMPWAIT_USE_DEFAULT_WAIT,
(LPSECURITY_ATTRIBUTES)NULL);
if((hPipeHandle1 == INVALID_HANDLE_VALUE) || (hPipeHandle1 == NULL))
{
err = GetLastError();
printf("Server Pipe Create Fail! err = %d\n", err);
exit(1);
}
else
printf("Server Pipe Create Success!");
while(TRUE)
{
//连接命名管道
rc = ConnectNamedPipe(hPipeHandle1, NULL);
if (rc == 0)
{
err = GetLastError();
printf("Server Pipe Connect Fail! err = %d\n", err);
exit(2);
}
else
printf("Server Pipe Connect Success!\n");
strcpy(InBuffer, "");
strcpy(OutBuffer, "");
//从命名管道中读数据
rc = ReadFile(hPipeHandle1,InBuffer, sizeof(InBuffer), &BytesRead,
(LPOVERLAPPED)NULL);
if (rc == 0 && BytesRead == 0)
{
err = GetLastError();
printf("Server Read Pipe Fail! err = %d\n", err);
exit(2);
}
else
printf("Server Read Pipe Success!DATA from Client is:\n %s\n",
InBuffer);
rc = strcmp(InBuffer, "end");
if (rc == 0)
break;
printf("Please Input Data to Send");
scanf("%s", OutBuffer);
//向命名管道中写数据
rc = WriteFile(hPipeHandle1, OutBuffer, sizeof(OutBuffer), &BytesWrite,
(LPOVERLAPPED)NULL);
if (rc == 0)
puts("Server Write Pipe Fail!");
else
puts("Server Write Pipe Success!");
//拆除与命名管道的连接
DisconnectNamedPipe(hPipeHandle1);
rc = strcmp(OutBuffer, "end");
if (rc == 0)
break;
}
printf("Now Server be END!");
CloseHandle(hPipeHandle1);
return nRetCode;
}
客户端程序:
#include "stdafx.h"
#include "042.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// The one and only application object
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
bool rc = 0;
char lpName[] = "\\\\.\\pipe\\myPipe";
char InBuffer[50] = "";
char OutBuffer[50] = "";
DWORD BytesRead;
int nRetCode = 0;
int err = 0;
while(1)
{
strcpy(InBuffer, "");
strcpy(OutBuffer, "");
printf("Input Data Please!");
scanf("%s", InBuffer);
rc = strcmp(InBuffer, "end");
if (rc == 0)
{
//连接命名管道
rc = CallNamedPipe(lpName, InBuffer, sizeof(InBuffer), OutBuffer,
sizeof(OutBuffer), &BytesRead, NMPWAIT_USE_DEFAULT_WAIT);
break;
}
//等待命名管道
rc = WaitNamedPipe(lpName, NMPWAIT_WAIT_FOREVER);
if (rc == 0)
{
err = GetLastError();
printf("Wait Pipe Fail! err = %d\n", err);
exit(1);
}
else
printf("Wait Pipe Success!\n");
rc = CallNamedPipe(lpName, InBuffer, sizeof(InBuffer), OutBuffer,
sizeof(OutBuffer), &BytesRead, NMPWAIT_USE_DEFAULT_WAIT);
rc = strcmp(OutBuffer, "end");
if (rc == 0)
break;
if (rc == 0)
{
err = GetLastError();
printf("Pipe Call Fail! err = %d\n", err);
exit(1);
}
else
printf("Pipe Call Success!\nData from Server is %s\n", OutBuffer);
}
puts("Now Client to be End!");
return nRetCode;
}
四、实验结果与总结
实验:
分别创建两个工程,一个作为服务器端程序另一个作为客户端程序,从而实现进程之间的通信。熟练掌握了 CreateNamePipe()函数,创建一个命名管道实例。服务器用函数 ConnectNamePipe()连接命名管道。在客户端,客户端使用 CallNamePipe()函数连接服务器建立的命名管道。客户端使用 WairNamedPipe()函数等待服务器连接命名管道。