操作系统 实验四:使用命名管道实现进程通信

*实验四:使用命名管道实现进程通信*

1 实验目的

(1)了解windows系统环境下的进程通讯机制。

(2)熟悉Windows系统提供的进程通信API。

2 实验准备知识:相关API函数介绍

1.建立命名管道

函数CreateNamePipe()创建一个命名管道实例,并返回该管道的句柄。

原型:

HANDLE CreateNamePipe(

LPCTSTR lpName, //命名管道的名字

DWORD dwOpenMode, //命名管道的访问模式

DWORD dwPipe’Mode, //命名管道的模式

DWORD nMaxInstances, //可创建实例的最大值

DWORD nOutBufferSize, //以字节为单位的输出缓冲区的大小

DWORD nInBufferSize, //以字节为单位的输入缓冲区的大小

DWORD nDefaultTimeOut, //默认超时时间

LPSECURITY_ATTRIBUTES lpSecurityAttributes //安全属性

);

参数说明:

lpName:为命名管道的名字,管道的命名方式为[\ServerName\pipe\pipename](file:///\ServerName\pipe\pipename),其中ServerName为用命名管道通信时服务器的主机名或IP地址,pipename为命名管道的名字,用户可自行定义。

dwOpenMode:指出命名管道的访问模式。模式如表2-5所示。

dwPipeMode:指出管道的模式。模式如表2-6所示、

nMaxInstances:该命名管道可以创建实例的最大值。

nOutBufferSize:输出缓冲区的大小,以字节为单位。

nInBufferSize:输入缓冲区的大小,以字节为单位。

nDefaultTimeOut:默认的超时时间,以ms为单位。如果函数WaitNamePipe()指出NMWAIT_USE_DEFAULT_WAIT,每个管道实例必须指定同一值的名字。

lpSecurityAttributes:为管道指定安全属性,为NULL时,管道得到一个默认的安全描述符。

返回值:

如果管道创建成功,将返回服务器命名管道实例的句柄。如果失败,返回INVALID_HANDLE_VALUE,可以调用函数GetLastError()查询失败的原因;当返回ERROR_INVALID_PARAMETER时,表明参数nMaxInstances指定的值大于PIPE_UNLIMTED_INSTANCES。

2.连接命名管道

服务器用函数ConnectNamePipe()连接命名管道。创建后命名管道也等待客户端的连接,客户端可以使用函数CreateFile()和CallNamedPipe()进行连接。

原型:

BOOL ConnectNamedPipe(

HANDLE hNamePipe, //命名管道实例句柄

LPOVERLAPPED lpOver lapped //指向Overlapped结构的指针

);
参数说明:

hNamedPipe:为命名管道创建时得到的一个命名管道实例句柄。

lpOverlapped:指向Overlapped结构的指针,可设其为NULL。

返回值:

成功,将返回一个非0值;失败,系统返回0,可以调用函数GetLastError()查询失败的原因。

3.拆除命名管道的连接

函数DisconnectNamePipe()拆除命名管道服务器与客户端的连接。

原型:

BOOL DisconnectNamePipe(

HANDLE hNamePipe

);

参数说明:

hNamedPipe:为命名管道创建时得到的一个命名管道实例句柄。

返回值:

成功,将返回一个非0值;失败,系统返回0,可以调用函数GerLasrError()查询失败的原因。

4.客户端连接服务器已建立的命名管道

客户端使用函数CallNamePipe()连接服务器建立的命名管道。

原型:

BOOL CallNamePipe(

LPCTSTR lpNamePipeName, //命名管道的名字

LPVOID lpInBuffer, //输出数据缓冲区

DWORD nInBufferSize, //以字节为单位的输出数据缓冲区的大小

LPVOID lpOurBuffer, //输入数据缓冲区指针

DWORD nOurBufferSize, //以字节为单位的输入数据缓冲区的大小

LPDWORD lpBytesRead, //输入字节数指针

DWORD nTimeOUT //等待时间

);

参数说明:

lpNamedPipeName:命名管道的名字。

lpInBuffer:指出用于输出数据(向管道写数据)的缓冲区指针。

nInBufferSize:用于输出数据缓冲区的大小,以字节为单位。

lpOutBuffer:指出用于接收数据(从管道读出数据)的缓冲区指针。

nOutBufferSize:指向用于接收数据缓冲区的大小,以字节为单位。

lpBytesRead:一个32位的变量,改变量用于存储从管道读出的字节数。

nTimeOut:等待命名管道成为可用状态的时间,单位为ms。

返回值:

成功,将返回一个非0值;失败,系统返回0,可用调用函数GetLastError()查询失败的原因。

5.客户端等待命名管道

客户端使用函数WaitNamedPipe()等待服务器连接命名管道。

原型:

BOOL WaitNamedPipe(

LPCTSTR lpNamedPipeName, //要等待的命名管道名

DWORD nTimeOut //等待时间

);

参数说明:

lpNamedPipeName:要等待的命名管道的名字。

nTimeOut:等待命名管道成为可用状态的时间,单位为ms.

返回值:

在等待时间内要连接的命名管道可以使用,将返回一个非0值。在等待时间内要连接的命名管道不可以使用,系统返回0,可用调用GetLastError()查询失败的原因。

3 实验内容

使用命名管道完成两个进程之间的通信。

4 实验要求

使用Windows系统提供的命名管道完成两个进程之间的通信,要求能正确使用创建命名管道CreateNamePipe()、连接命名管道ConnectNamePipe()、拆除命名道的连接DisconnectNamePipe()、连接服务器已建立的命名管道CallNamePipe()、等待命名管道WaitNamedPipe()等API.

5 实验指导

完成两个进程之间的通信,需要建立两个过程文件,在Microsoft Visual C++6.0环境下建立服务器工程文件PipeServer和客户端工程文件PipeClienr。在服务器程序中,首先使用CreateNamePipe()建立一个命名管道,之后使用ConnectNamePipe()连接命名管道,如果命名管道连接成功,可以使用读文件函数ReadFile()从管道中读取数据,并可使用写文件函数WriteFile()向管道中写入数据,管道使用完毕后可以使用DisconnectNamePipe()

拆除与命名管道的连接。在客户端程序中,可以使用WaitNamedPipe()等待服务器建立好命名管道,然后使用CallNamePipe()与服务器建立命名管道的连接,并同时得到服务器发来的数据或向服务器发送数据,如图2-6所示。

6 实验总结

该实验完成了两个进程的通信,请同学们在下面程序的基础上增加和完善程序的功能,如设计一个聊天室等,使其可以实现自己的设计需求。命名管道创建命名CreateNamePipe()也可以使用文件创建命名CreateFile()来实现其功能,同学们可以自己去尝试,命名管道创建命名CreateNamePipe()中的参数比较多,请同学们仔细研究其含义,使用不当可能会导致两个进程通信的失败。

图2-7所示为客户端程序运行情况,首先在客户端输入数据HalloSever!,按回车键后结果见图2-8,可以看到客户端输入的数据已经在服务器显示出来。同样在服务器输入数据HelloClient!,在客户端同样也显示出来,这说明建立的命名管道已经做到了双向通信。

imag

图2

imge

7 源程序

/*服务器程序/

// PipeServe.cpp : Defines the entry point for the console application.

//

#include “stdafx.h”

#include “PipeServe.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!\nDATA from Client is = %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;

}

/客户端程序/

//PipeClient.cpp : Defines the entry point for the console application.

//

#include “stdafx.h”

#include “PipeClient.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;

}

8实验展望

在完成以上实验后,可以对Windows系统提供的进程通信API有一定的了解,在此基础上设计并完成一个综合性的实验,解决实际的进程通信问题,除了可以使用上面的命名管道外,也可以使用无名管道(Pipe)、文件映射(FileMapping)及套接字(Soket)等进程通讯工具。在完成以上实验的基础上总结一下Windows 提供了哪些进程通讯工具,并比较这些进程通讯工具的优缺点和应用场合。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值